diff options
Diffstat (limited to 'libexec')
| -rw-r--r-- | libexec/lfs_cleanerd/cleaner.h | 3 | ||||
| -rw-r--r-- | libexec/lfs_cleanerd/coalesce.c | 85 | ||||
| -rw-r--r-- | libexec/lfs_cleanerd/lfs_cleanerd.c | 35 |
3 files changed, 93 insertions, 30 deletions
diff --git a/libexec/lfs_cleanerd/cleaner.h b/libexec/lfs_cleanerd/cleaner.h index a6daf4c3102..9238fa3705d 100644 --- a/libexec/lfs_cleanerd/cleaner.h +++ b/libexec/lfs_cleanerd/cleaner.h @@ -69,14 +69,13 @@ int load_segment(struct clfs *, int, BLOCK_INFO **, int *); int needs_cleaning(struct clfs *, CLEANERINFO *); int reinit_fs(struct clfs *); void reload_ifile(struct clfs *); -void toss_old_blocks(struct clfs *, BLOCK_INFO **, int *, int *); +void toss_old_blocks(struct clfs *, BLOCK_INFO **, blkcnt_t *, int *); /* cleansrv.c */ void check_control_socket(void); void try_to_become_master(int, char **); /* coalesce.c */ -int log2int(int); int clean_all_inodes(struct clfs *); int fork_coalesce(struct clfs *); diff --git a/libexec/lfs_cleanerd/coalesce.c b/libexec/lfs_cleanerd/coalesce.c index 37bf0c14f1b..9c5467182ae 100644 --- a/libexec/lfs_cleanerd/coalesce.c +++ b/libexec/lfs_cleanerd/coalesce.c @@ -1,4 +1,4 @@ -/* $NetBSD: coalesce.c,v 1.28 2015/07/28 05:14:23 dholland Exp $ */ +/* $NetBSD: coalesce.c,v 1.29 2015/08/12 18:23:16 dholland Exp $ */ /*- * Copyright (c) 2002, 2005 The NetBSD Foundation, Inc. @@ -60,7 +60,12 @@ extern int debug, do_mmap; -int log2int(int n) +/* + * XXX return the arg to just int when/if we don't need it for + * potentially huge block counts any more. + */ +static int +log2int(intmax_t n) { int log; @@ -151,8 +156,8 @@ clean_inode(struct clfs *fs, ino_t ino) int blkcnt; } */ lim; daddr_t toff; - int i; - int nb, onb, noff; + int noff; + blkcnt_t i, nb, onb; int retval; int bps; @@ -178,16 +183,36 @@ clean_inode(struct clfs *fs, ino_t ino) } #endif if (nb > dip->di_blocks) { - dlog("ino %d, computed blocks %d > held blocks %d", ino, nb, - dip->di_blocks); + dlog("ino %ju, computed blocks %jd > held blocks %ju", + (uintmax_t)ino, (intmax_t)nb, + (uintmax_t)dip->di_blocks); free(dip); return COALESCE_BADBLOCKSIZE; } + /* + * XXX: We should really coalesce really large files in + * chunks, as there's substantial diminishing returns and + * mallocing huge amounts of memory just to get those returns + * is pretty silly. But that requires a big rework of this + * code. (On the plus side though then we can stop worrying + * about block counts > 2^31.) + */ + + /* ugh, no DADDR_T_MAX */ + __CTASSERT(sizeof(daddr_t) == sizeof(int64_t)); + if (nb > INT64_MAX / sizeof(BLOCK_INFO)) { + syslog(LOG_WARNING, "ino %ju, %jd blocks: array too large\n", + (uintmax_t)ino, (uintmax_t)nb); + free(dip); + return COALESCE_NOMEM; + } + bip = (BLOCK_INFO *)malloc(sizeof(BLOCK_INFO) * nb); if (bip == NULL) { - syslog(LOG_WARNING, "ino %llu, %d blocks: %m", - (unsigned long long)ino, nb); + syslog(LOG_WARNING, "ino %llu, %jd blocks: %s\n", + (unsigned long long)ino, (intmax_t)nb, + strerror(errno)); free(dip); return COALESCE_NOMEM; } @@ -198,6 +223,18 @@ clean_inode(struct clfs *fs, ino_t ino) bip[i].bi_version = dip->di_gen; /* Don't set the size, but let lfs_bmap fill it in */ } + /* + * The kernel also contains this check; but as lim.blkcnt is + * only 32 bits wide, we need to check ourselves too in case + * we'd otherwise truncate a value > 2^31, as that might + * succeed and create bizarre results. + */ + if (nb > LFS_MARKV_MAXBLKCNT) { + syslog(LOG_WARNING, "%s: coalesce: LFCNBMAPV: Too large\n", + lfs_sb_getfsmnt(fs)); + retval = COALESCE_BADBMAPV; + goto out; + } lim.blkiov = bip; lim.blkcnt = nb; if (kops.ko_fcntl(fs->clfs_ifilefd, LFCNBMAPV, &lim) < 0) { @@ -208,13 +245,15 @@ clean_inode(struct clfs *fs, ino_t ino) } #if 0 for (i = 0; i < nb; i++) { - printf("bi_size = %d, bi_ino = %d, " - "bi_lbn = %d, bi_daddr = %d\n", - bip[i].bi_size, bip[i].bi_inode, bip[i].bi_lbn, - bip[i].bi_daddr); + printf("bi_size = %d, bi_ino = %ju, " + "bi_lbn = %jd, bi_daddr = %jd\n", + bip[i].bi_size, (uintmax_t)bip[i].bi_inode, + (intmax_t)bip[i].bi_lbn, + (intmax_t)bip[i].bi_daddr); } #endif - noff = toff = 0; + noff = 0; + toff = 0; for (i = 1; i < nb; i++) { if (bip[i].bi_daddr != bip[i - 1].bi_daddr + lfs_sb_getfrag(fs)) ++noff; @@ -235,8 +274,8 @@ clean_inode(struct clfs *fs, ino_t ino) goto out; } else if (debug) syslog(LOG_DEBUG, "ino %llu total discontinuity " - "%d (%lld) for %d blocks", (unsigned long long)ino, - noff, (long long)toff, nb); + "%d (%jd) for %jd blocks", (unsigned long long)ino, + noff, (intmax_t)toff, (intmax_t)nb); /* Search for blocks in active segments; don't move them. */ for (i = 0; i < nb; i++) { @@ -274,8 +313,8 @@ clean_inode(struct clfs *fs, ino_t ino) for (i = 0; i < nb; i++) { bip[i].bi_bp = malloc(bip[i].bi_size); if (bip[i].bi_bp == NULL) { - syslog(LOG_WARNING, "allocate block buffer size=%d: %m", - bip[i].bi_size); + syslog(LOG_WARNING, "allocate block buffer size=%d: %s\n", + bip[i].bi_size, strerror(errno)); retval = COALESCE_NOMEM; goto out; } @@ -287,13 +326,16 @@ clean_inode(struct clfs *fs, ino_t ino) } } if (debug) - syslog(LOG_DEBUG, "ino %llu markv %d blocks", - (unsigned long long)ino, nb); + syslog(LOG_DEBUG, "ino %ju markv %jd blocks", + (uintmax_t)ino, (intmax_t)nb); /* * Write in segment-sized chunks. If at any point we'd write more * than half of the available segments, sleep until that's not * true any more. + * + * XXX the pointer arithmetic in this loop is illegal; replace + * TBIP with an integer (blkcnt_t) offset. */ bps = lfs_segtod(fs, 1); for (tbip = bip; tbip < bip + nb; tbip += bps) { @@ -307,6 +349,11 @@ clean_inode(struct clfs *fs, ino_t ino) LFCNSEGWAIT, NULL); } while(cip.clean < 4); + /* + * Note that although lim.blkcnt is 32 bits wide, bps + * (which is blocks-per-segment) is < 2^32 so the + * value assigned here is always in range. + */ lim.blkiov = tbip; lim.blkcnt = (tbip + bps < bip + nb ? bps : nb % bps); if (kops.ko_fcntl(fs->clfs_ifilefd, LFCNMARKV, &lim) < 0) { diff --git a/libexec/lfs_cleanerd/lfs_cleanerd.c b/libexec/lfs_cleanerd/lfs_cleanerd.c index 4f574e3e535..2918ecc8ee0 100644 --- a/libexec/lfs_cleanerd/lfs_cleanerd.c +++ b/libexec/lfs_cleanerd/lfs_cleanerd.c @@ -1,4 +1,4 @@ -/* $NetBSD: lfs_cleanerd.c,v 1.44 2015/08/02 18:18:09 dholland Exp $ */ +/* $NetBSD: lfs_cleanerd.c,v 1.45 2015/08/12 18:23:16 dholland Exp $ */ /*- * Copyright (c) 2005 The NetBSD Foundation, Inc. @@ -791,7 +791,7 @@ bi_comparator(const void *va, const void *vb) return -1; if (b->bi_lbn == LFS_UNUSED_LBN) return 1; - if ((u_int32_t)a->bi_lbn > (u_int32_t)b->bi_lbn) + if ((u_int64_t)a->bi_lbn > (u_int64_t)b->bi_lbn) return 1; else return -1; @@ -813,9 +813,10 @@ cb_comparator(const void *va, const void *vb) } void -toss_old_blocks(struct clfs *fs, BLOCK_INFO **bipp, int *bic, int *sizep) +toss_old_blocks(struct clfs *fs, BLOCK_INFO **bipp, blkcnt_t *bic, int *sizep) { - int i, r; + blkcnt_t i; + int r; BLOCK_INFO *bip = *bipp; struct lfs_fcntl_markv /* { BLOCK_INFO *blkiov; @@ -832,6 +833,13 @@ toss_old_blocks(struct clfs *fs, BLOCK_INFO **bipp, int *bic, int *sizep) for (i = 0; i < *bic; i++) bip[i].bi_segcreate = bip[i].bi_daddr; + /* + * XXX: blkcnt_t is 64 bits, so *bic might overflow size_t + * (the argument type of heapsort's number argument) on a + * 32-bit platform. However, if so we won't have got this far + * because we'll have failed trying to allocate the array. So + * while *bic here might cause a 64->32 truncation, it's safe. + */ /* Sort the blocks */ heapsort(bip, *bic, sizeof(BLOCK_INFO), bi_comparator); @@ -870,6 +878,7 @@ invalidate_segment(struct clfs *fs, int sn) { BLOCK_INFO *bip; int i, r, bic; + blkcnt_t widebic; off_t nb; double util; struct lfs_fcntl_markv /* { @@ -884,7 +893,9 @@ invalidate_segment(struct clfs *fs, int sn) fs->clfs_nactive = 0; if (load_segment(fs, sn, &bip, &bic) <= 0) return -1; - toss_old_blocks(fs, &bip, &bic, NULL); + widebic = bic; + toss_old_blocks(fs, &bip, &widebic, NULL); + bic = widebic; /* Record statistics */ for (i = nb = 0; i < bic; i++) @@ -925,7 +936,7 @@ invalidate_segment(struct clfs *fs, int sn) * if the block needs to be added, 0 if it is already represented. */ static int -check_or_add(ino_t ino, int32_t lbn, BLOCK_INFO *bip, int bic, BLOCK_INFO **ebipp, int *ebicp) +check_or_add(ino_t ino, daddr_t lbn, BLOCK_INFO *bip, int bic, BLOCK_INFO **ebipp, int *ebicp) { BLOCK_INFO *t, *ebip = *ebipp; int ebic = *ebicp; @@ -975,7 +986,7 @@ check_hidden_cost(struct clfs *fs, BLOCK_INFO *bip, int bic, off_t *ifc) int num; int i, j, ebic; BLOCK_INFO *ebip; - int32_t lbn; + daddr_t lbn; start = 0; ebip = NULL; @@ -998,6 +1009,7 @@ check_hidden_cost(struct clfs *fs, BLOCK_INFO *bip, int bic, off_t *ifc) if (bip[i].bi_lbn < ULFS_NDADDR) continue; + /* XXX the struct lfs cast is completely wrong/unsafe */ ulfs_getlbns((struct lfs *)fs, NULL, (daddr_t)bip[i].bi_lbn, in, &num); for (j = 0; j < num; j++) { check_or_add(bip[i].bi_inode, in[j].in_lbn, @@ -1016,6 +1028,7 @@ int clean_fs(struct clfs *fs, CLEANERINFO *cip) { int i, j, ngood, sn, bic, r, npos; + blkcnt_t widebic; int bytes, totbytes; struct ubuf *bp; SEGUSE *sup; @@ -1096,7 +1109,9 @@ clean_fs(struct clfs *fs, CLEANERINFO *cip) fs->clfs_segtabp[i]->nbytes); if ((r = load_segment(fs, sn, &bip, &bic)) > 0) { ++ngood; - toss_old_blocks(fs, &bip, &bic, &bytes); + widebic = bic; + toss_old_blocks(fs, &bip, &widebic, &bytes); + bic = widebic; totbytes += bytes; } else if (r == 0) fd_release(fs->clfs_devvp); @@ -1124,7 +1139,9 @@ clean_fs(struct clfs *fs, CLEANERINFO *cip) else break; } - toss_old_blocks(fs, &bip, &bic, NULL); + widebic = bic; + toss_old_blocks(fs, &bip, &widebic, NULL); + bic = widebic; } /* If there is nothing to do, try again later. */ |
