From da0cdd33148ab3e3c227355de759c595c533841a Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Mon, 24 Jul 2017 19:05:33 -0700 Subject: [PATCH] hammer2 - Initial HARDLINK -> DIRENT replacement code * Initial removal of the vestiges of the old embedded inode code. Inodes were moved to the root directory long ago but directories still contain dummy OBJTYPE_HARDLINK inodes instead of real directory entries to point to the moved inodes. These inodes ate 1024 bytes of disk space for each directory entry. * Remove the dummy OBJTYPE_HARDLINK inodes and replace with new BREF_TYPE_DIRENT blockrefs. These blockrefs represent directory entries, and the entire dirent will fit in the blockref (requiring no data ref) if the filename is <= 64 bytes. * This new DIRENT mechanic significantly improves performance and reduces storage overage vs the previous mechanicn, for obvious reasons. Directory entries are now 128 bytes instead of 1024 bytes, and since they are collected together in indirect blocks or (if <= 4 entries) simply placed in the 4 blockrefs embedded in the directory inode, the related I/O tends to be fairly optimal. Only directory entries whos filenames are > 64 bytes long require an additional data block reference. For now, due to other constraints, we use the minimum H2 allocation size of 1KB for these, so certainly space is wasted. But in real life there aren't actually a whole lot of filenames that are that long so it should be fine. --- lib/libstand/hammer2.c | 134 ++++++++++++++---- sbin/hammer2/cmd_debug.c | 47 +++++-- sbin/hammer2/subs.c | 2 - sys/vfs/hammer2/hammer2.h | 25 +++- sys/vfs/hammer2/hammer2_bulkfree.c | 1 + sys/vfs/hammer2/hammer2_chain.c | 211 +++++++++++++++++++++-------- sys/vfs/hammer2/hammer2_disk.h | 15 +- sys/vfs/hammer2/hammer2_flush.c | 10 ++ sys/vfs/hammer2/hammer2_freemap.c | 21 ++- sys/vfs/hammer2/hammer2_inode.c | 192 +++++++++++++++++++++++--- sys/vfs/hammer2/hammer2_io.c | 1 + sys/vfs/hammer2/hammer2_ioctl.c | 11 +- sys/vfs/hammer2/hammer2_strategy.c | 4 +- sys/vfs/hammer2/hammer2_subr.c | 24 ++-- sys/vfs/hammer2/hammer2_synchro.c | 46 ++++--- sys/vfs/hammer2/hammer2_vfsops.c | 1 + sys/vfs/hammer2/hammer2_vnops.c | 82 ++++++----- sys/vfs/hammer2/hammer2_xops.c | 180 +++++++++++++----------- 18 files changed, 709 insertions(+), 298 deletions(-) diff --git a/lib/libstand/hammer2.c b/lib/libstand/hammer2.c index 3f9361dd62..fb2885cb95 100644 --- a/lib/libstand/hammer2.c +++ b/lib/libstand/hammer2.c @@ -140,7 +140,12 @@ static size_t blocksize(hammer2_blockref_t *bref) { - return(1 << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX)); + size_t bytes; + + bytes = (size_t)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + if (bytes) + bytes = (size_t)1 << bytes; + return bytes; } static @@ -279,6 +284,8 @@ h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base, int dev_boff; int dev_bsize; + bref_ret->type = 0; + if (base == NULL) { saved_base.data_off = (hammer2_off_t)-1; return(0); @@ -299,6 +306,9 @@ h2lookup(struct hammer2_fs *hfs, hammer2_blockref_t *base, case HAMMER2_BREF_TYPE_INDIRECT: count = blocksize(base) / sizeof(hammer2_blockref_t); break; + default: + count = 0; + break; } /* @@ -316,8 +326,9 @@ again: */ if (base->type != HAMMER2_BREF_TYPE_VOLUME && base->data_off != saved_base.data_off) { - if (h2read(hfs, &media, - blocksize(base), blockoff(base))) { + if (blocksize(base) && h2read(hfs, &media, + blocksize(base), + blockoff(base))) { return(-1); } saved_base = *base; @@ -388,18 +399,24 @@ again: goto again; } break; + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: case HAMMER2_BREF_TYPE_DATA: /* * Terminal match. Leaf elements might not be data-aligned. */ dev_bsize = blocksize(&best); - if (dev_bsize < HAMMER2_LBUFSIZE) - dev_bsize = HAMMER2_LBUFSIZE; - dev_boff = blockoff(&best) - - (blockoff(&best) & ~HAMMER2_LBUFMASK64); - if (h2read(hfs, &media, dev_bsize, blockoff(&best) - dev_boff)) - return(-1); + if (dev_bsize) { + if (dev_bsize < HAMMER2_LBUFSIZE) + dev_bsize = HAMMER2_LBUFSIZE; + dev_boff = blockoff(&best) - + (blockoff(&best) & ~HAMMER2_LBUFMASK64); + if (h2read(hfs, &media, + dev_bsize, + blockoff(&best) - dev_boff)) { + return(-1); + } + } saved_base.data_off = (hammer2_off_t)-1; *bref_ret = best; *pptr = media.buf + dev_boff; @@ -417,12 +434,14 @@ h2resolve(struct hammer2_fs *hfs, const char *path, hammer2_blockref_t bres; hammer2_inode_data_t *ino; hammer2_key_t key; + void *data; ssize_t bytes; size_t len; /* * Start point (superroot) */ + ino = NULL; *bref = hfs->sroot; if (inop) *inop = NULL; @@ -447,23 +466,62 @@ h2resolve(struct hammer2_fs *hfs, const char *path, for (;;) { bytes = h2lookup(hfs, bref, key, key | 0xFFFFU, - &bres, (void **)&ino); - if (bytes == 0) + &bres, (void **)&data); + if (bytes < 0) + break; + if (bres.type == 0) + break; + switch (bres.type) { + case HAMMER2_BREF_TYPE_DIRENT: + if (bres.embed.dirent.namlen != len) + break; + if (bres.embed.dirent.namlen <= + sizeof(bres.check.buf)) { + if (memcmp(path, bres.check.buf, len)) + break; + } else { + if (memcmp(path, data, len)) + break; + } + + /* + * Found, resolve inode. This will set + * ino similarly to HAMMER2_BREF_TYPE_INODE + * and adjust bres, which path continuation + * needs. + */ + *bref = hfs->sroot; + bytes = h2lookup(hfs, bref, + bres.embed.dirent.inum, + bres.embed.dirent.inum, + &bres, (void **)&ino); + goto found; + break; /* NOT REACHED */ + case HAMMER2_BREF_TYPE_INODE: + ino = data; + if (ino->meta.name_len != len) + break; + if (memcmp(path, ino->filename, len) == 0) { + if (inop) + *inop = ino; + goto found; + } break; - if (len == ino->meta.name_len && - memcmp(path, ino->filename, len) == 0) { - if (inop) - *inop = ino; + } + if ((bres.key & 0xFFFF) == 0xFFFF) { + bres.type = 0; break; } key = bres.key + 1; } +found: /* * Lookup failure */ - if (bytes == 0) { + if (bytes < 0 || bres.type == 0) { bref->data_off = (hammer2_off_t)-1; + ino = NULL; break; } @@ -723,8 +781,6 @@ hammer2_get_dtype(uint8_t type) return(DT_BLK); case HAMMER2_OBJTYPE_SOFTLINK: return(DT_LNK); - case HAMMER2_OBJTYPE_HARDLINK: - return(DT_UNKNOWN); case HAMMER2_OBJTYPE_SOCKET: return(DT_SOCK); default: @@ -749,8 +805,6 @@ hammer2_get_mode(uint8_t type) return(S_IFBLK); case HAMMER2_OBJTYPE_SOFTLINK: return(S_IFLNK); - case HAMMER2_OBJTYPE_HARDLINK: - return(0); case HAMMER2_OBJTYPE_SOCKET: return(S_IFSOCK); default: @@ -868,20 +922,46 @@ hammer2_readdir(struct open_file *f, struct dirent *den) struct hfile *hf = f->f_fsdata; hammer2_blockref_t bres; hammer2_inode_data_t *ipdata; + void *data; int bytes; for (;;) { bytes = h2lookup(&hf->hfs, &hf->bref, f->f_offset | HAMMER2_DIRHASH_VISIBLE, HAMMER2_KEY_MAX, - &bres, (void **)&ipdata); - if (bytes <= 0) + &bres, (void **)&data); + if (bytes < 0) break; - den->d_namlen = ipdata->meta.name_len; - den->d_type = hammer2_get_dtype(ipdata->meta.type); - den->d_ino = ipdata->meta.inum; - bcopy(ipdata->filename, den->d_name, den->d_namlen); - den->d_name[den->d_namlen] = 0; + switch (bres.type) { + case HAMMER2_BREF_TYPE_INODE: + ipdata = data; + den->d_namlen = ipdata->meta.name_len; + den->d_type = hammer2_get_dtype(ipdata->meta.type); + den->d_ino = ipdata->meta.inum; + bcopy(ipdata->filename, den->d_name, den->d_namlen); + den->d_name[den->d_namlen] = 0; + break; + case HAMMER2_BREF_TYPE_DIRENT: + den->d_namlen = bres.embed.dirent.namlen; + den->d_type = hammer2_get_dtype(bres.embed.dirent.type); + den->d_ino = bres.embed.dirent.inum; + if (den->d_namlen <= sizeof(bres.check.buf)) { + bcopy(bres.check.buf, + den->d_name, + den->d_namlen); + } else { + bcopy(data, den->d_name, den->d_namlen); + } + den->d_name[den->d_namlen] = 0; + break; + default: + den->d_namlen = 1; + den->d_type = + hammer2_get_dtype(HAMMER2_OBJTYPE_REGFILE); + den->d_name[0] = '?'; + den->d_name[1] = 0; + break; + } f->f_offset = bres.key + 1; diff --git a/sbin/hammer2/cmd_debug.c b/sbin/hammer2/cmd_debug.c index 5345dc7c8f..637195c38c 100644 --- a/sbin/hammer2/cmd_debug.c +++ b/sbin/hammer2/cmd_debug.c @@ -443,6 +443,9 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) case HAMMER2_BREF_TYPE_EMPTY: type_str = "empty"; break; + case HAMMER2_BREF_TYPE_DIRENT: + type_str = "dirent"; + break; case HAMMER2_BREF_TYPE_INODE: type_str = "inode"; break; @@ -477,9 +480,10 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) if (bref->flags) printf("flags=%02x ", bref->flags); - bytes = (size_t)1 << (bref->data_off & HAMMER2_OFF_MASK_RADIX); - - { + bytes = (bref->data_off & HAMMER2_OFF_MASK_RADIX); + if (bytes) + bytes = (size_t)1 << bytes; + if (bytes) { hammer2_off_t io_off; hammer2_off_t io_base; size_t io_bytes; @@ -519,8 +523,12 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) * a quick meta-data scan. Meta-data integrity is always checked. * (Also see the check above that ensures the media data is loaded, * otherwise there's no data to check!). + * + * WARNING! bref->check state may be used for other things when + * bref has no data (bytes == 0). */ - if (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1) { + if (bytes && + (bref->type != HAMMER2_BREF_TYPE_DATA || VerboseOpt >= 1)) { switch(HAMMER2_DEC_CHECK(bref->methods)) { case HAMMER2_CHECK_NONE: printf("(meth %02x) ", bref->methods); @@ -574,6 +582,24 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) case HAMMER2_BREF_TYPE_EMPTY: obrace = 0; break; + case HAMMER2_BREF_TYPE_DIRENT: + printf("{\n"); + if (bref->embed.dirent.namlen <= sizeof(bref->check.buf)) { + tabprintf(tab, "filename \"%*.*s\"\n", + bref->embed.dirent.namlen, + bref->embed.dirent.namlen, + bref->check.buf); + } else { + tabprintf(tab, "filename \"%*.*s\"\n", + bref->embed.dirent.namlen, + bref->embed.dirent.namlen, + media.buf); + } + tabprintf(tab, "inum 0x%016jx\n", + (uintmax_t)bref->embed.dirent.inum); + tabprintf(tab, "type %s\n", + hammer2_iptype_to_str(bref->embed.dirent.type)); + break; case HAMMER2_BREF_TYPE_INODE: printf("{\n"); if (media.ipdata.meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA) { @@ -608,13 +634,8 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) hammer2_uuid_to_str(&media.ipdata.meta.uid, &str)); tabprintf(tab, "gid %s\n", hammer2_uuid_to_str(&media.ipdata.meta.gid, &str)); - if (media.ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) - tabprintf(tab, "type %s (%s)\n", - hammer2_iptype_to_str(media.ipdata.meta.type), - hammer2_iptype_to_str(media.ipdata.meta.target_type)); - else - tabprintf(tab, "type %s\n", - hammer2_iptype_to_str(media.ipdata.meta.type)); + tabprintf(tab, "type %s\n", + hammer2_iptype_to_str(media.ipdata.meta.type)); tabprintf(tab, "opflgs 0x%02x\n", media.ipdata.meta.op_flags); tabprintf(tab, "capflgs 0x%04x\n", @@ -658,11 +679,11 @@ show_bref(int fd, int tab, int bi, hammer2_blockref_t *bref, int dofreemap) tabprintf(tab, "data_quota %ju\n", (uintmax_t)media.ipdata.meta.data_quota); tabprintf(tab, "data_count %ju\n", - (uintmax_t)bref->data_count); + (uintmax_t)bref->embed.stats.data_count); tabprintf(tab, "inode_quota %ju\n", (uintmax_t)media.ipdata.meta.inode_quota); tabprintf(tab, "inode_count %ju\n", - (uintmax_t)bref->inode_count); + (uintmax_t)bref->embed.stats.inode_count); break; case HAMMER2_BREF_TYPE_INDIRECT: bscan = &media.npdata[0]; diff --git a/sbin/hammer2/subs.c b/sbin/hammer2/subs.c index 492796764a..168f6960ef 100644 --- a/sbin/hammer2/subs.c +++ b/sbin/hammer2/subs.c @@ -186,8 +186,6 @@ hammer2_iptype_to_str(uint8_t type) return("BDEV"); case HAMMER2_OBJTYPE_SOFTLINK: return("SOFTLINK"); - case HAMMER2_OBJTYPE_HARDLINK: - return("HARDLINK"); case HAMMER2_OBJTYPE_SOCKET: return("SOCKET"); case HAMMER2_OBJTYPE_WHITEOUT: diff --git a/sys/vfs/hammer2/hammer2.h b/sys/vfs/hammer2/hammer2.h index 5d0ed2918b..ecafffdc12 100644 --- a/sys/vfs/hammer2/hammer2.h +++ b/sys/vfs/hammer2/hammer2.h @@ -941,6 +941,12 @@ struct hammer2_xop_lookup { hammer2_key_t lhc; }; +struct hammer2_xop_mkdirent { + hammer2_xop_head_t head; + hammer2_dirent_head_t dirent; + hammer2_key_t lhc; +}; + struct hammer2_xop_create { hammer2_xop_head_t head; hammer2_inode_meta_t meta; /* initial metadata */ @@ -981,6 +987,7 @@ typedef struct hammer2_xop_unlink hammer2_xop_unlink_t; typedef struct hammer2_xop_nrename hammer2_xop_nrename_t; typedef struct hammer2_xop_ipcluster hammer2_xop_ipcluster_t; typedef struct hammer2_xop_strategy hammer2_xop_strategy_t; +typedef struct hammer2_xop_mkdirent hammer2_xop_mkdirent_t; typedef struct hammer2_xop_create hammer2_xop_create_t; typedef struct hammer2_xop_destroy hammer2_xop_destroy_t; typedef struct hammer2_xop_fsync hammer2_xop_fsync_t; @@ -999,6 +1006,7 @@ union hammer2_xop { hammer2_xop_unlink_t xop_unlink; hammer2_xop_nrename_t xop_nrename; hammer2_xop_strategy_t xop_strategy; + hammer2_xop_mkdirent_t xop_mkdirent; hammer2_xop_create_t xop_create; hammer2_xop_destroy_t xop_destroy; hammer2_xop_fsync_t xop_fsync; @@ -1323,7 +1331,7 @@ void hammer2_dev_exlock(hammer2_dev_t *hmp); void hammer2_dev_shlock(hammer2_dev_t *hmp); void hammer2_dev_unlock(hammer2_dev_t *hmp); -int hammer2_get_dtype(const hammer2_inode_data_t *ipdata); +int hammer2_get_dtype(uint8_t type); int hammer2_get_vtype(uint8_t type); uint8_t hammer2_get_obj_type(enum vtype vtype); void hammer2_time_to_timespec(uint64_t xtime, struct timespec *ts); @@ -1367,6 +1375,8 @@ hammer2_inode_t *hammer2_inode_create(hammer2_inode_t *dip, int flags, int *errorp); void hammer2_inode_chain_sync(hammer2_inode_t *ip); int hammer2_inode_unlink_finisher(hammer2_inode_t *ip, int isopen); +int hammer2_dirent_create(hammer2_inode_t *dip, const char *name, + size_t name_len, hammer2_key_t inum, uint8_t type); /* * hammer2_chain.c @@ -1394,15 +1404,15 @@ hammer2_media_data_t *hammer2_chain_wdata(hammer2_chain_t *chain); int hammer2_chain_snapshot(hammer2_chain_t *chain, hammer2_ioc_pfs_t *pmp, hammer2_tid_t mtid); -int hammer2_chain_hardlink_find(hammer2_chain_t **parentp, - hammer2_chain_t **chainp, - int clindex, int flags); +int hammer2_chain_inode_find(hammer2_pfs_t *pmp, hammer2_key_t inum, + int clindex, int flags, + hammer2_chain_t **parentp, + hammer2_chain_t **chainp); void hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, hammer2_off_t dedup_off, int flags); void hammer2_chain_modify_ip(hammer2_inode_t *ip, hammer2_chain_t *chain, hammer2_tid_t mtid, int flags); -void hammer2_chain_resize(hammer2_inode_t *ip, hammer2_chain_t *parent, - hammer2_chain_t *chain, +void hammer2_chain_resize(hammer2_chain_t *chain, hammer2_tid_t mtid, hammer2_off_t dedup_off, int nradix, int flags); void hammer2_chain_unlock(hammer2_chain_t *chain); @@ -1446,6 +1456,8 @@ void hammer2_chain_bulkdrop(hammer2_chain_t *copy); void hammer2_chain_setcheck(hammer2_chain_t *chain, void *bdata); int hammer2_chain_testcheck(hammer2_chain_t *chain, void *bdata); +int hammer2_chain_dirent_test(hammer2_chain_t *chain, const char *name, + size_t name_len); void hammer2_pfs_memory_wait(hammer2_pfs_t *pmp); void hammer2_pfs_memory_inc(hammer2_pfs_t *pmp); @@ -1572,6 +1584,7 @@ void hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_xop_scanlhc(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_xop_scanall(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_xop_lookup(hammer2_thread_t *thr, hammer2_xop_t *xop); +void hammer2_inode_xop_mkdirent(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_inode_xop_create(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_inode_xop_destroy(hammer2_thread_t *thr, hammer2_xop_t *xop); void hammer2_inode_xop_chain_sync(hammer2_thread_t *thr, hammer2_xop_t *xop); diff --git a/sys/vfs/hammer2/hammer2_bulkfree.c b/sys/vfs/hammer2/hammer2_bulkfree.c index 07ba7208db..3744f389f7 100644 --- a/sys/vfs/hammer2/hammer2_bulkfree.c +++ b/sys/vfs/hammer2/hammer2_bulkfree.c @@ -515,6 +515,7 @@ h2_bulkfree_callback(hammer2_bulkfree_info_t *cbinfo, hammer2_blockref_t *bref) * it's a problem if it does. (Or L0 (2MB) for that matter). */ radix = (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + KKASSERT(radix != 0); bytes = (size_t)1 << radix; class = (bref->type << 8) | hammer2_devblkradix(radix); diff --git a/sys/vfs/hammer2/hammer2_chain.c b/sys/vfs/hammer2/hammer2_chain.c index f3461c78f4..2c5104d124 100644 --- a/sys/vfs/hammer2/hammer2_chain.c +++ b/sys/vfs/hammer2/hammer2_chain.c @@ -166,7 +166,17 @@ hammer2_chain_alloc(hammer2_dev_t *hmp, hammer2_pfs_t *pmp, hammer2_blockref_t *bref) { hammer2_chain_t *chain; - u_int bytes = 1U << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + u_int bytes; + + /* + * Special case - radix of 0 indicates a chain that does not + * need a data reference (context is completely embedded in the + * bref). + */ + if ((int)(bref->data_off & HAMMER2_OFF_MASK_RADIX)) + bytes = 1U << (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + else + bytes = 0; atomic_add_long(&hammer2_chain_allocs, 1); @@ -174,6 +184,7 @@ hammer2_chain_alloc(hammer2_dev_t *hmp, hammer2_pfs_t *pmp, * Construct the appropriate system structure. */ switch(bref->type) { + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: case HAMMER2_BREF_TYPE_INDIRECT: case HAMMER2_BREF_TYPE_FREEMAP_NODE: @@ -838,7 +849,9 @@ hammer2_chain_lock(hammer2_chain_t *chain, int how) TIMER(22); /* - * Do we have to resolve the data? + * Do we have to resolve the data? This is generally only + * applicable to HAMMER2_BREF_TYPE_DATA which is special-cased. + * Other BREF types expects the data to be there. */ switch(how & HAMMER2_RESOLVE_MASK) { case HAMMER2_RESOLVE_NEVER: @@ -939,10 +952,13 @@ hammer2_chain_load_data(hammer2_chain_t *chain) int error; /* - * Degenerate case, data already present. + * Degenerate case, data already present, or chain is not expected + * to have any data. */ if (chain->data) return; + if ((chain->bref.data_off & HAMMER2_OFF_MASK_RADIX) == 0) + return; TIMER(23); hmp = chain->hmp; @@ -1094,8 +1110,11 @@ hammer2_chain_load_data(hammer2_chain_t *chain) /* * Copy data from bp to embedded buffer */ - panic("hammer2_chain_lock: called on unresolved volume header"); + panic("hammer2_chain_load_data: unresolved volume header"); break; + case HAMMER2_BREF_TYPE_DIRENT: + KKASSERT(chain->bytes != 0); + /* fall through */ case HAMMER2_BREF_TYPE_INODE: case HAMMER2_BREF_TYPE_FREEMAP_LEAF: case HAMMER2_BREF_TYPE_INDIRECT: @@ -1314,7 +1333,7 @@ hammer2_chain_countbrefs(hammer2_chain_t *chain, /* * Resize the chain's physical storage allocation in-place. This function does - * not adjust the data pointer and must be followed by (typically) a + * not usually adjust the data pointer and must be followed by (typically) a * hammer2_chain_modify() call to copy any old data over and adjust the * data pointer. * @@ -1322,6 +1341,10 @@ hammer2_chain_countbrefs(hammer2_chain_t *chain, * larger will reallocate the storage. Excess or prior storage is reclaimed * asynchronously at a later time. * + * An nradix value of 0 is special-cased to mean that the storage should + * be disassociated, that is the chain is being resized to 0 bytes (not 1 + * byte). + * * Must be passed an exclusively locked parent and chain. * * This function is mostly used with DATA blocks locked RESOLVE_NEVER in order @@ -1332,8 +1355,7 @@ hammer2_chain_countbrefs(hammer2_chain_t *chain, * XXX return error if cannot resize. */ void -hammer2_chain_resize(hammer2_inode_t *ip, - hammer2_chain_t *parent, hammer2_chain_t *chain, +hammer2_chain_resize(hammer2_chain_t *chain, hammer2_tid_t mtid, hammer2_off_t dedup_off, int nradix, int flags) { @@ -1349,14 +1371,14 @@ hammer2_chain_resize(hammer2_inode_t *ip, */ KKASSERT(chain != &hmp->vchain); KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_DATA || - chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT); - KKASSERT(chain->parent == parent); + chain->bref.type == HAMMER2_BREF_TYPE_INDIRECT || + chain->bref.type == HAMMER2_BREF_TYPE_DIRENT); /* * Nothing to do if the element is already the proper size */ obytes = chain->bytes; - nbytes = 1U << nradix; + nbytes = (nradix) ? (1U << nradix) : 0; if (obytes == nbytes) return; @@ -1374,11 +1396,12 @@ hammer2_chain_resize(hammer2_inode_t *ip, * Relocate the block, even if making it smaller (because different * block sizes may be in different regions). * - * (data blocks only, we aren't copying the storage here). + * NOTE: Operation does not copy the data and may only be used + * to resize data blocks in-place, or directory entry blocks + * which are about to be modified in some manner. */ hammer2_freemap_alloc(chain, nbytes); chain->bytes = nbytes; - /*ip->delta_dcount += (ssize_t)(nbytes - obytes);*/ /* XXX atomic */ /* * We don't want the followup chain_modify() to try to copy data @@ -1387,7 +1410,8 @@ hammer2_chain_resize(hammer2_inode_t *ip, * originator already has the data to write in-hand. */ if (chain->dio) { - KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_DATA); + KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_DATA || + chain->bref.type == HAMMER2_BREF_TYPE_DIRENT); hammer2_io_brelse(&chain->dio); chain->data = NULL; } @@ -1406,7 +1430,15 @@ modified_needs_new_allocation(hammer2_chain_t *chain) /* * We only live-dedup data, we do not live-dedup meta-data. */ - if (chain->bref.type != HAMMER2_BREF_TYPE_DATA) + if (chain->bref.type != HAMMER2_BREF_TYPE_DATA && + chain->bref.type != HAMMER2_BREF_TYPE_DIRENT) { + return 0; + } + + /* + * If chain has no data, then there is nothing to live-dedup. + */ + if (chain->bytes == 0) return 0; /* @@ -1485,7 +1517,8 @@ hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, * Data must be resolved if already assigned, unless explicitly * flagged otherwise. */ - if (chain->data == NULL && (flags & HAMMER2_MODIFY_OPTDATA) == 0 && + if (chain->data == NULL && chain->bytes != 0 && + (flags & HAMMER2_MODIFY_OPTDATA) == 0 && (chain->bref.data_off & ~HAMMER2_OFF_MASK_RADIX)) { hammer2_chain_load_data(chain); } @@ -1519,7 +1552,8 @@ hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, * NOTE! This data-block cannot be used as a de-duplication * source when the check mode is set to NONE. */ - if (chain->bref.type == HAMMER2_BREF_TYPE_DATA && + if ((chain->bref.type == HAMMER2_BREF_TYPE_DATA || + chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) && (chain->flags & HAMMER2_CHAIN_INITIAL) == 0 && HAMMER2_DEC_CHECK(chain->bref.methods) == HAMMER2_CHECK_NONE && @@ -1560,10 +1594,13 @@ hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, * to reset it to a fully allocated state to force two bulkfree * passes to free it again. * + * NOTE: Only applicable when chain->bytes != 0. + * * XXX can a chain already be marked MODIFIED without a data * assignment? If not, assert here instead of testing the case. */ - if (chain != &hmp->vchain && chain != &hmp->fchain) { + if (chain != &hmp->vchain && chain != &hmp->fchain && + chain->bytes) { if ((chain->bref.data_off & ~HAMMER2_OFF_MASK_RADIX) == 0 || newmod ) { @@ -1612,6 +1649,8 @@ hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, * that the modifications are being done via the logical buffer cache. * The INITIAL flag relates only to the device data buffer and thus * remains unchange in this situation. + * + * This code also handles bytes == 0 (most dirents). */ if (chain->bref.type == HAMMER2_BREF_TYPE_DATA && (flags & HAMMER2_MODIFY_OPTDATA) && @@ -1647,6 +1686,15 @@ hammer2_chain_modify(hammer2_chain_t *chain, hammer2_tid_t mtid, */ KKASSERT(chain->dio == NULL); break; + case HAMMER2_BREF_TYPE_DIRENT: + /* + * The data might be fully embedded. + */ + if (chain->bytes == 0) { + KKASSERT(chain->dio == NULL); + break; + } + /* fall through */ case HAMMER2_BREF_TYPE_INODE: case HAMMER2_BREF_TYPE_FREEMAP_LEAF: case HAMMER2_BREF_TYPE_DATA: @@ -2854,10 +2902,11 @@ hammer2_chain_create(hammer2_chain_t **parentp, hammer2_chain_t **chainp, * processed when we call hammer2_chain_modify(). * * Recalculate bytes to reflect the actual media block - * allocation. + * allocation. Handle special case radix 0 == 0 bytes. */ - bytes = (hammer2_off_t)1 << - (int)(chain->bref.data_off & HAMMER2_OFF_MASK_RADIX); + bytes = (size_t)(chain->bref.data_off & HAMMER2_OFF_MASK_RADIX); + if (bytes) + bytes = (hammer2_off_t)1 << bytes; chain->bytes = bytes; switch(type) { @@ -2876,6 +2925,7 @@ hammer2_chain_create(hammer2_chain_t **parentp, hammer2_chain_t **chainp, case HAMMER2_BREF_TYPE_FREEMAP_LEAF: KKASSERT(bytes == sizeof(chain->data->bmdata)); /* fall through */ + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: case HAMMER2_BREF_TYPE_DATA: default: @@ -3028,6 +3078,7 @@ again: switch(chain->bref.type) { case HAMMER2_BREF_TYPE_DATA: case HAMMER2_BREF_TYPE_FREEMAP_LEAF: + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: hammer2_chain_modify(chain, mtid, dedup_off, HAMMER2_MODIFY_OPTDATA); @@ -3120,11 +3171,14 @@ hammer2_chain_rename(hammer2_blockref_t *bref, * Now create a duplicate of the chain structure, associating * it with the same core, making it the same size, pointing it * to the same bref (the same media block). + * + * NOTE: Handle special radix == 0 case (means 0 bytes). */ if (bref == NULL) bref = &chain->bref; - bytes = (hammer2_off_t)1 << - (int)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + bytes = (size_t)(bref->data_off & HAMMER2_OFF_MASK_RADIX); + if (bytes) + bytes = (hammer2_off_t)1 << bytes; /* * If parent is not NULL the duplicated chain will be entered under @@ -3205,10 +3259,7 @@ _hammer2_chain_delete_helper(hammer2_chain_t *parent, hammer2_chain_t *chain, /* * Access the inode's block array. However, there * is no block array if the inode is flagged - * DIRECTDATA. The DIRECTDATA case typicaly only - * occurs when a hardlink has been shifted up the - * tree and the original inode gets replaced with - * an OBJTYPE_HARDLINK placeholding inode. + * DIRECTDATA. */ if (parent->data && (parent->data->ipdata.meta.op_flags & @@ -3449,10 +3500,14 @@ hammer2_chain_create_indirect(hammer2_chain_t *parent, keybits = hammer2_chain_indkey_file(parent, &key, keybits, base, count, ncount); break; + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: keybits = hammer2_chain_indkey_dir(parent, &key, keybits, base, count, ncount); break; + default: + panic("illegal indirect block for bref type %d", for_type); + break; } /* @@ -4552,10 +4607,14 @@ hammer2_base_delete(hammer2_chain_t *parent, } /* - * Update stats and zero the entry + * Update stats and zero the entry. + * + * NOTE: Handle radix == 0 (0 bytes) case. */ - parent->bref.embed.stats.data_count -= (hammer2_off_t)1 << - (int)(scan->data_off & HAMMER2_OFF_MASK_RADIX); + if ((int)(scan->data_off & HAMMER2_OFF_MASK_RADIX)) { + parent->bref.embed.stats.data_count -= (hammer2_off_t)1 << + (int)(scan->data_off & HAMMER2_OFF_MASK_RADIX); + } switch(scan->type) { case HAMMER2_BREF_TYPE_INODE: parent->bref.embed.stats.inode_count -= 1; @@ -4637,8 +4696,10 @@ hammer2_base_insert(hammer2_chain_t *parent, /* * Update stats and zero the entry */ - parent->bref.embed.stats.data_count += (hammer2_off_t)1 << - (int)(elm->data_off & HAMMER2_OFF_MASK_RADIX); + if ((int)(elm->data_off & HAMMER2_OFF_MASK_RADIX)) { + parent->bref.embed.stats.data_count += (hammer2_off_t)1 << + (int)(elm->data_off & HAMMER2_OFF_MASK_RADIX); + } switch(elm->type) { case HAMMER2_BREF_TYPE_INODE: parent->bref.embed.stats.inode_count += 1; @@ -5005,26 +5066,26 @@ hammer2_chain_testcheck(hammer2_chain_t *chain, void *bdata) } /* - * The caller presents a shared-locked (parent, chain) where the chain - * is of type HAMMER2_OBJTYPE_HARDLINK. + * Acquire the chain and parent representing the specified inode for the + * device at the specified cluster index. * - * The flags passed in are LOOKUP flags, not RESOLVE flags. Only - * HAMMER2_LOOKUP_SHARED is supported. + * The flags passed in are LOOKUP flags, not RESOLVE flags. * - * We locate the actual inode chain & parent. + * If we are unable to locate the hardlink, INVAL is returned and *chainp + * will be NULL. *parentp may still be set error or not, or NULL if the + * parent itself could not be resolved. * - * If we are unable to locate the hardlink, EIO is returned and - * (*chainp) is unlocked and dropped. + * Caller must pass-in a valid or NULL *parentp or *chainp. The passed-in + * *parentp and *chainp will be unlocked if not NULL. */ int -hammer2_chain_hardlink_find(hammer2_chain_t **parentp, hammer2_chain_t **chainp, - int clindex, int flags) +hammer2_chain_inode_find(hammer2_pfs_t *pmp, hammer2_key_t inum, + int clindex, int flags, + hammer2_chain_t **parentp, hammer2_chain_t **chainp) { hammer2_chain_t *parent; hammer2_chain_t *rchain; - hammer2_pfs_t *pmp; hammer2_key_t key_dummy; - hammer2_key_t lhc; int cache_index = -1; int resolve_flags; @@ -5032,28 +5093,33 @@ hammer2_chain_hardlink_find(hammer2_chain_t **parentp, hammer2_chain_t **chainp, HAMMER2_RESOLVE_SHARED : 0; /* - * Obtain the key for the hardlink from *chainp. - */ - rchain = *chainp; - pmp = rchain->pmp; - lhc = rchain->data->ipdata.meta.inum; - hammer2_chain_unlock(rchain); - hammer2_chain_drop(rchain); - rchain = NULL; - - /* - * Hardlinks hang off of iroot + * Caller expects us to replace these. */ + if (*chainp) { + hammer2_chain_unlock(*chainp); + hammer2_chain_drop(*chainp); + *chainp = NULL; + } if (*parentp) { hammer2_chain_unlock(*parentp); hammer2_chain_drop(*parentp); + *parentp = NULL; } + + /* + * Inodes hang off of the iroot (bit 63 is clear, differentiating + * inodes from root directory entries in the key lookup). + */ parent = hammer2_inode_chain(pmp->iroot, clindex, resolve_flags); - rchain = hammer2_chain_lookup(&parent, &key_dummy, - lhc, lhc, - &cache_index, flags); + rchain = NULL; + if (parent) { + rchain = hammer2_chain_lookup(&parent, &key_dummy, + inum, inum, + &cache_index, flags); + } *parentp = parent; *chainp = rchain; + return (rchain ? 0 : EINVAL); } @@ -5203,3 +5269,36 @@ hammer2_chain_snapshot(hammer2_chain_t *chain, hammer2_ioc_pfs_t *pmp, } return (error); } + +/* + * Returns non-zero if the chain (INODE or DIRENT) matches the + * filename. + */ +int +hammer2_chain_dirent_test(hammer2_chain_t *chain, const char *name, + size_t name_len) +{ + const hammer2_inode_data_t *ripdata; + const hammer2_dirent_head_t *den; + + if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) { + ripdata = &chain->data->ipdata; + if (ripdata->meta.name_len == name_len && + bcmp(ripdata->filename, name, name_len) == 0) { + return 1; + } + } + if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT && + chain->bref.embed.dirent.namlen == name_len) { + den = &chain->bref.embed.dirent; + if (name_len > sizeof(chain->bref.check.buf) && + bcmp(chain->data->buf, name, name_len) == 0) { + return 1; + } + if (name_len <= sizeof(chain->bref.check.buf) && + bcmp(chain->bref.check.buf, name, name_len) == 0) { + return 1; + } + } + return 0; +} diff --git a/sys/vfs/hammer2/hammer2_disk.h b/sys/vfs/hammer2/hammer2_disk.h index 5c9d9113e1..0f831095de 100644 --- a/sys/vfs/hammer2/hammer2_disk.h +++ b/sys/vfs/hammer2/hammer2_disk.h @@ -436,6 +436,10 @@ typedef uint32_t hammer2_crc32_t; * minimum allocation chunk size is 1KB (a radix of 10), so HAMMER2 sets * HAMMER2_RADIX_MIN to 10. The maximum radix is currently 16 (64KB), but * we fully intend to support larger extents in the future. + * + * WARNING! A radix of 0 (such as when data_off is all 0's) is a special + * case which means no data associated with the blockref, and + * not the '1 byte' it would otherwise calculate to. */ #define HAMMER2_OFF_BAD ((hammer2_off_t)-1) #define HAMMER2_OFF_MASK 0xFFFFFFFFFFFFFFC0ULL @@ -525,7 +529,6 @@ typedef struct dmsg_lnk_hammer2_volconf dmsg_lnk_hammer2_volconf_t; #define H2_LNK_VOLCONF(msg) ((dmsg_lnk_hammer2_volconf_t *)(msg)->any.buf) -#if 0 /* * HAMMER2 directory entry header (embedded in blockref) exactly 16 bytes */ @@ -539,8 +542,6 @@ struct hammer2_dirent_head { typedef struct hammer2_dirent_head hammer2_dirent_head_t; -#endif - /* * The media block reference structure. This forms the core of the HAMMER2 * media topology recursion. This 128-byte data structure is embedded in the @@ -608,7 +609,7 @@ struct hammer2_blockref { /* MUST BE EXACTLY 64 BYTES */ hammer2_tid_t update_tid; /* clc modify (propagated upward) */ union { char buf[16]; -#if 0 + /* * Directory entry header (BREF_TYPE_DIRENT) * @@ -623,7 +624,7 @@ struct hammer2_blockref { /* MUST BE EXACTLY 64 BYTES */ * allows both cases. */ hammer2_dirent_head_t dirent; -#endif + /* * Statistics aggregation (BREF_TYPE_INODE, BREF_TYPE_INDIRECT) */ @@ -687,7 +688,7 @@ typedef struct hammer2_blockref hammer2_blockref_t; #define HAMMER2_BREF_TYPE_INODE 1 #define HAMMER2_BREF_TYPE_INDIRECT 2 #define HAMMER2_BREF_TYPE_DATA 3 -#define HAMMER2_BREF_TYPE_UNUSED04 4 +#define HAMMER2_BREF_TYPE_DIRENT 4 #define HAMMER2_BREF_TYPE_FREEMAP_NODE 5 #define HAMMER2_BREF_TYPE_FREEMAP_LEAF 6 #define HAMMER2_BREF_TYPE_FREEMAP 254 /* pseudo-type */ @@ -1013,7 +1014,7 @@ typedef struct hammer2_inode_data hammer2_inode_data_t; #define HAMMER2_OBJTYPE_CDEV 5 #define HAMMER2_OBJTYPE_BDEV 6 #define HAMMER2_OBJTYPE_SOFTLINK 7 -#define HAMMER2_OBJTYPE_HARDLINK 8 /* dummy entry for hardlink */ +#define HAMMER2_OBJTYPE_UNUSED08 8 #define HAMMER2_OBJTYPE_SOCKET 9 #define HAMMER2_OBJTYPE_WHITEOUT 10 diff --git a/sys/vfs/hammer2/hammer2_flush.c b/sys/vfs/hammer2/hammer2_flush.c index e21441a157..c73eaf5373 100644 --- a/sys/vfs/hammer2/hammer2_flush.c +++ b/sys/vfs/hammer2/hammer2_flush.c @@ -769,6 +769,16 @@ again: KKASSERT((chain->flags & HAMMER2_CHAIN_EMBEDDED) == 0); hammer2_chain_setcheck(chain, chain->data); break; + case HAMMER2_BREF_TYPE_DIRENT: + /* + * A directory entry can use the check area to store + * the filename for filenames <= 64 bytes, don't blow + * it up! + */ + KKASSERT((chain->flags & HAMMER2_CHAIN_EMBEDDED) == 0); + if (chain->bytes) + hammer2_chain_setcheck(chain, chain->data); + break; case HAMMER2_BREF_TYPE_INODE: /* * NOTE: We must call io_setdirty() to make any late diff --git a/sys/vfs/hammer2/hammer2_freemap.c b/sys/vfs/hammer2/hammer2_freemap.c index facdb72c70..b00cef7c40 100644 --- a/sys/vfs/hammer2/hammer2_freemap.c +++ b/sys/vfs/hammer2/hammer2_freemap.c @@ -192,6 +192,8 @@ hammer2_freemap_reserve(hammer2_chain_t *chain, int radix) * * ip and bpref are only used as a heuristic to determine locality of * reference. bref->key may also be used heuristically. + * + * This function is a NOP if bytes is 0. */ int hammer2_freemap_alloc(hammer2_chain_t *chain, size_t bytes) @@ -205,6 +207,15 @@ hammer2_freemap_alloc(hammer2_chain_t *chain, size_t bytes) unsigned int hindex; hammer2_fiterate_t iter; + /* + * If allocating or downsizing to zero we just get rid of whatever + * data_off we had. + */ + if (bytes == 0) { + chain->bref.data_off = 0; + return 0; + } + mtid = hammer2_trans_sub(hmp->spmp); /* @@ -810,6 +821,8 @@ hammer2_freemap_iterate(hammer2_chain_t **parentp, hammer2_chain_t **chainp, * as allocated) blocks whos freemap upates might not have been committed * in the last crash and is used by the bulk freemap scan to stage frees. * + * WARNING! Cannot be called with a empty-data bref (radix == 0). + * * XXX currently disabled when how == 0 (the normal real-time case). At * the moment we depend on the bulk freescan to actually free blocks. It * will still call this routine with a non-zero how to stage possible frees @@ -849,14 +862,18 @@ hammer2_freemap_adjust(hammer2_dev_t *hmp, hammer2_blockref_t *bref, mtid = hammer2_trans_sub(hmp->spmp); radix = (int)data_off & HAMMER2_OFF_MASK_RADIX; + KKASSERT(radix != 0); data_off &= ~HAMMER2_OFF_MASK_RADIX; KKASSERT(radix <= HAMMER2_RADIX_MAX); - bytes = (size_t)1 << radix; + if (radix) + bytes = (size_t)1 << radix; + else + bytes = 0; class = (bref->type << 8) | hammer2_devblkradix(radix); /* - * We can't adjust thre freemap for data allocations made by + * We can't adjust the freemap for data allocations made by * newfs_hammer2. */ if (data_off < hmp->voldata.allocator_beg) diff --git a/sys/vfs/hammer2/hammer2_inode.c b/sys/vfs/hammer2/hammer2_inode.c index 8b09c9f41e..e9211770c6 100644 --- a/sys/vfs/hammer2/hammer2_inode.c +++ b/sys/vfs/hammer2/hammer2_inode.c @@ -645,14 +645,15 @@ again: } /* - * Create a new inode in the specified directory using the vattr to - * figure out the type. A non-zero type field overrides vattr. + * MESSY! CLEANUP! + * + * Create a new inode using the vattr to figure out the type. A non-zero + * type field overrides vattr. We need the directory to set iparent or to + * use when the inode is directly embedded in a directory (typically super-root + * entries), but note that this really only applies OBJTYPE_DIRECTORY as + * non-directory inodes can be hardlinked. * * If no error occurs the new inode with its cluster locked is returned. - * However, when creating an OBJTYPE_HARDLINK, the caller can assume - * that NULL will be returned (that is, the caller already has the inode - * in-hand and is creating a hardlink to it, we do not need to return a - * representitive ip). * * If vap and/or cred are NULL the related fields are not set and the * inode type defaults to a directory. This is used when creating PFSs @@ -664,13 +665,9 @@ again: * super-root entries for snapshots and PFSs. When used to create a * snapshot the inode will be temporarily associated with the spmp. * - * NOTE: When creating a normal file or directory the caller must call this - * function twice, once to create the actual inode and once to create - * the hardlink representing the directory entry. This function is - * only called once when creating a softlink. The softlink itself. - * - * NOTE: When creating a hardlink target (a real inode), name/name_len is - * passed as NULL/0, and caller should pass lhc as inum. + * NOTE: When creating a normal file or directory the name/name_len/lhc + * is optional, but is typically specified to make debugging and + * recovery easeier. */ hammer2_inode_t * hammer2_inode_create(hammer2_inode_t *dip, hammer2_inode_t *pip, @@ -816,8 +813,7 @@ hammer2_inode_create(hammer2_inode_t *dip, hammer2_inode_t *pip, * the size is extended past the embedded limit. */ if (xop->meta.type == HAMMER2_OBJTYPE_REGFILE || - xop->meta.type == HAMMER2_OBJTYPE_SOFTLINK || - xop->meta.type == HAMMER2_OBJTYPE_HARDLINK) { + xop->meta.type == HAMMER2_OBJTYPE_SOFTLINK) { xop->meta.op_flags |= HAMMER2_OPFLAG_DIRECTDATA; } if (name) { @@ -854,13 +850,8 @@ hammer2_inode_create(hammer2_inode_t *dip, hammer2_inode_t *pip, * * NOTE: nipdata will have chain's blockset data. */ - if (type != HAMMER2_OBJTYPE_HARDLINK) { - nip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster, -1); - nip->comp_heuristic = 0; - } else { - nip = NULL; - } - + nip = hammer2_inode_get(dip->pmp, dip, &xop->head.cluster, -1); + nip->comp_heuristic = 0; done: hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); done2: @@ -869,6 +860,91 @@ done2: return (nip); } +/* + * Create a directory entry under dip with the specified name, inode number, + * and OBJTYPE (type). + */ +int +hammer2_dirent_create(hammer2_inode_t *dip, const char *name, size_t name_len, + hammer2_key_t inum, uint8_t type) +{ + hammer2_xop_mkdirent_t *xop; + hammer2_key_t lhc; + int error; + + lhc = 0; + error = 0; + + KKASSERT(name != NULL); + lhc = hammer2_dirhash(name, name_len); + + /* + * Locate the inode or indirect block to create the new + * entry in. At the same time check for key collisions + * and iterate until we don't get one. + * + * Lock the directory exclusively for now to guarantee that + * we can find an unused lhc for the name. Due to collisions, + * two different creates can end up with the same lhc so we + * cannot depend on the OS to prevent the collision. + */ + hammer2_inode_lock(dip, 0); + + /* + * If name specified, locate an unused key in the collision space. + * Otherwise use the passed-in lhc directly. + */ + { + hammer2_xop_scanlhc_t *sxop; + hammer2_key_t lhcbase; + + lhcbase = lhc; + sxop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING); + sxop->lhc = lhc; + hammer2_xop_start(&sxop->head, hammer2_xop_scanlhc); + while ((error = hammer2_xop_collect(&sxop->head, 0)) == 0) { + if (lhc != sxop->head.cluster.focus->bref.key) + break; + ++lhc; + } + hammer2_xop_retire(&sxop->head, HAMMER2_XOPMASK_VOP); + + if (error) { + if (error != ENOENT) + goto done2; + ++lhc; + error = 0; + } + if ((lhcbase ^ lhc) & ~HAMMER2_DIRHASH_LOMASK) { + error = ENOSPC; + goto done2; + } + } + + /* + * Create the directory entry with the lhc as the key. + */ + xop = hammer2_xop_alloc(dip, HAMMER2_XOP_MODIFYING); + xop->lhc = lhc; + bzero(&xop->dirent, sizeof(xop->dirent)); + xop->dirent.inum = inum; + xop->dirent.type = type; + xop->dirent.namlen = name_len; + + KKASSERT(name_len < HAMMER2_INODE_MAXNAME); + hammer2_xop_setname(&xop->head, name, name_len); + + hammer2_xop_start(&xop->head, hammer2_inode_xop_mkdirent); + + error = hammer2_xop_collect(&xop->head, 0); + + hammer2_xop_retire(&xop->head, HAMMER2_XOPMASK_VOP); +done2: + hammer2_inode_unlock(dip); + + return error; +} + /* * Repoint ip->cluster's chains to cluster's chains and fixup the default * focus. All items, valid or invalid, are repointed. hammer2_xop_start() @@ -1210,6 +1286,77 @@ hammer2_inode_run_sideq(hammer2_pfs_t *pmp) LOCKSTOP; } +/* + * Helper to create a directory entry. + */ +void +hammer2_inode_xop_mkdirent(hammer2_thread_t *thr, hammer2_xop_t *arg) +{ + hammer2_xop_mkdirent_t *xop = &arg->xop_mkdirent; + hammer2_chain_t *parent; + hammer2_chain_t *chain; + hammer2_key_t key_next; + size_t data_len; + int cache_index = -1; + int error; + + if (hammer2_debug & 0x0001) + kprintf("dirent_create lhc %016jx clindex %d\n", + xop->lhc, thr->clindex); + + parent = hammer2_inode_chain(xop->head.ip1, thr->clindex, + HAMMER2_RESOLVE_ALWAYS); + if (parent == NULL) { + error = EIO; + chain = NULL; + goto fail; + } + chain = hammer2_chain_lookup(&parent, &key_next, + xop->lhc, xop->lhc, + &cache_index, 0); + if (chain) { + error = EEXIST; + goto fail; + } + + /* + * We may be able to embed the directory entry directly in the + * blockref. + */ + if (xop->dirent.namlen <= sizeof(chain->bref.check.buf)) + data_len = 0; + else + data_len = HAMMER2_ALLOC_MIN; + + error = hammer2_chain_create(&parent, &chain, + xop->head.ip1->pmp, HAMMER2_METH_DEFAULT, + xop->lhc, 0, + HAMMER2_BREF_TYPE_DIRENT, + data_len, + xop->head.mtid, 0, 0); + if (error == 0) { + hammer2_chain_modify(chain, xop->head.mtid, 0, 0); + + chain->bref.embed.dirent = xop->dirent; + if (xop->dirent.namlen <= sizeof(chain->bref.check.buf)) + bcopy(xop->head.name1, chain->bref.check.buf, + xop->dirent.namlen); + else + bcopy(xop->head.name1, chain->data->buf, + xop->dirent.namlen); + } +fail: + if (parent) { + hammer2_chain_unlock(parent); + hammer2_chain_drop(parent); + } + hammer2_xop_feed(&xop->head, chain, thr->clindex, error); + if (chain) { + hammer2_chain_unlock(chain); + hammer2_chain_drop(chain); + } +} + /* * Inode create helper (threaded, backend) * @@ -1510,6 +1657,7 @@ hammer2_inode_xop_chain_sync(hammer2_thread_t *thr, hammer2_xop_t *arg) * Degenerate embedded case, nothing to loop on */ switch (chain->bref.type) { + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: KKASSERT(0); break; diff --git a/sys/vfs/hammer2/hammer2_io.c b/sys/vfs/hammer2/hammer2_io.c index 45556c1662..00fdb0cbaa 100644 --- a/sys/vfs/hammer2/hammer2_io.c +++ b/sys/vfs/hammer2/hammer2_io.c @@ -1119,6 +1119,7 @@ dio_write_stats_update(hammer2_io_t *dio) case HAMMER2_BREF_TYPE_DATA: counterp = &hammer2_iod_file_write; break; + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: counterp = &hammer2_iod_meta_write; break; diff --git a/sys/vfs/hammer2/hammer2_ioctl.c b/sys/vfs/hammer2/hammer2_ioctl.c index c93bd1505d..4335c31b28 100644 --- a/sys/vfs/hammer2/hammer2_ioctl.c +++ b/sys/vfs/hammer2/hammer2_ioctl.c @@ -536,14 +536,8 @@ hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data) &cache_index, HAMMER2_LOOKUP_SHARED); while (chain) { - if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) { - ripdata = &chain->data->ipdata; - if (ripdata->meta.name_len == len && - bcmp(ripdata->filename, pfs->name, len) == 0) { - break; - } - ripdata = NULL; /* safety */ - } + if (hammer2_chain_dirent_test(chain, pfs->name, len)) + break; chain = hammer2_chain_next(&parent, chain, &key_next, key_next, lhc + HAMMER2_DIRHASH_LOMASK, @@ -555,6 +549,7 @@ hammer2_ioctl_pfs_lookup(hammer2_inode_t *ip, void *data) * Load the data being returned by the ioctl. */ if (chain) { + KKASSERT(chain->bref.type == HAMMER2_BREF_TYPE_INODE); ripdata = &chain->data->ipdata; pfs->name_key = ripdata->meta.name_key; pfs->pfs_type = ripdata->meta.pfs_type; diff --git a/sys/vfs/hammer2/hammer2_strategy.c b/sys/vfs/hammer2/hammer2_strategy.c index f98d5dc6c9..ded6d2d4e2 100644 --- a/sys/vfs/hammer2/hammer2_strategy.c +++ b/sys/vfs/hammer2/hammer2_strategy.c @@ -435,7 +435,7 @@ hammer2_strategy_read_completion(hammer2_chain_t *chain, char *data, if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) { /* - * Data is embedded in the inode (copy from inode). + * Copy from in-memory inode structure. */ bcopy(((hammer2_inode_data_t *)data)->u.data, bp->b_data, HAMMER2_EMBEDDED_BYTES); @@ -774,7 +774,7 @@ retry: dedup_off = hammer2_dedup_lookup(chain->hmp, datap, pblksize); if (chain->bytes != pblksize) { - hammer2_chain_resize(ip, *parentp, chain, + hammer2_chain_resize(chain, mtid, dedup_off, pradix, HAMMER2_MODIFY_OPTDATA); diff --git a/sys/vfs/hammer2/hammer2_subr.c b/sys/vfs/hammer2/hammer2_subr.c index d7cc3eedae..daeb89df19 100644 --- a/sys/vfs/hammer2/hammer2_subr.c +++ b/sys/vfs/hammer2/hammer2_subr.c @@ -69,13 +69,8 @@ hammer2_dev_unlock(hammer2_dev_t *hmp) * ip must be locked sh/ex. */ int -hammer2_get_dtype(const hammer2_inode_data_t *ipdata) +hammer2_get_dtype(uint8_t type) { - uint8_t type; - - if ((type = ipdata->meta.type) == HAMMER2_OBJTYPE_HARDLINK) - type = ipdata->meta.target_type; - switch(type) { case HAMMER2_OBJTYPE_UNKNOWN: return (DT_UNKNOWN); @@ -91,8 +86,6 @@ hammer2_get_dtype(const hammer2_inode_data_t *ipdata) return (DT_BLK); case HAMMER2_OBJTYPE_SOFTLINK: return (DT_LNK); - case HAMMER2_OBJTYPE_HARDLINK: /* (never directly associated w/vp) */ - return (DT_UNKNOWN); case HAMMER2_OBJTYPE_SOCKET: return (DT_SOCK); case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ @@ -124,8 +117,6 @@ hammer2_get_vtype(uint8_t type) return (VBLK); case HAMMER2_OBJTYPE_SOFTLINK: return (VLNK); - case HAMMER2_OBJTYPE_HARDLINK: /* XXX */ - return (VBAD); case HAMMER2_OBJTYPE_SOCKET: return (VSOCK); case HAMMER2_OBJTYPE_WHITEOUT: /* not supported */ @@ -294,13 +285,19 @@ hammer2_allocsize(size_t bytes) #endif /* - * Convert bytes to radix with no limitations + * Convert bytes to radix with no limitations. + * + * 0 bytes is special-cased to a radix of zero (which would normally + * translate to (1 << 0) == 1). */ int hammer2_getradix(size_t bytes) { int radix; + /* + * Optimize the iteration by pre-checking commonly used radii. + */ if (bytes == HAMMER2_PBUFSIZE) radix = HAMMER2_PBUFRADIX; else if (bytes >= HAMMER2_LBUFSIZE) @@ -310,6 +307,10 @@ hammer2_getradix(size_t bytes) else radix = 0; + /* + * Iterate as needed. Note that bytes == 0 is expected to return + * a radix of 0 as a special case. + */ while (((size_t)1 << radix) < bytes) ++radix; return (radix); @@ -379,6 +380,7 @@ hammer2_adjreadcounter(hammer2_blockref_t *bref, size_t bytes) case HAMMER2_BREF_TYPE_DATA: counterp = &hammer2_iod_file_read; break; + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_INODE: counterp = &hammer2_iod_meta_read; break; diff --git a/sys/vfs/hammer2/hammer2_synchro.c b/sys/vfs/hammer2/hammer2_synchro.c index 6ae6ce715a..fcecb4238b 100644 --- a/sys/vfs/hammer2/hammer2_synchro.c +++ b/sys/vfs/hammer2/hammer2_synchro.c @@ -528,14 +528,8 @@ hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip, * update to compatible content first but we do not * synchronize modify_tid until the entire recursion * has completed successfully. - * - * NOTE: Do not try to access hardlink pointers as if - * they were normal inodes, the inode cache will - * get seriously confused. */ - if (focus->bref.type == HAMMER2_BREF_TYPE_INODE && - focus->data->ipdata.meta.type != - HAMMER2_OBJTYPE_HARDLINK) { + if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) { nerror = hammer2_sync_replace( thr, parent, chain, 0, @@ -564,14 +558,8 @@ hammer2_sync_slaves(hammer2_thread_t *thr, hammer2_inode_t *ip, * compatible content first but we do not synchronize * modify_tid until the entire recursion has * completed successfully. - * - * NOTE: Do not try to access hardlink pointers as if - * they were normal inodes, the inode cache will - * get seriously confused. */ - if (focus->bref.type == HAMMER2_BREF_TYPE_INODE && - focus->data->ipdata.meta.type != - HAMMER2_OBJTYPE_HARDLINK) { + if (focus->bref.type == HAMMER2_BREF_TYPE_INODE) { nerror = hammer2_sync_insert( thr, &parent, &chain, 0, @@ -804,6 +792,18 @@ hammer2_sync_insert(hammer2_thread_t *thr, bcopy(focus->data, chain->data, chain->bytes); hammer2_chain_setcheck(chain, chain->data); break; + case HAMMER2_BREF_TYPE_DIRENT: + /* + * Directory entries embed data in the blockref. + */ + if (chain->bytes) { + bcopy(focus->data, chain->data, chain->bytes); + hammer2_chain_setcheck(chain, chain->data); + } else { + chain->bref.check = focus->bref.check; + } + chain->bref.embed = focus->bref.embed; + break; default: KKASSERT(0); break; @@ -813,7 +813,7 @@ hammer2_sync_insert(hammer2_thread_t *thr, *chainp = chain; /* will be returned locked */ /* - * Avoid ordering deadlock when relocking shared. + * Avoid an ordering deadlock when relocking shared. */ hammer2_chain_unlock(*parentp); hammer2_chain_lock(*parentp, HAMMER2_RESOLVE_SHARED | @@ -910,9 +910,7 @@ hammer2_sync_replace(hammer2_thread_t *thr, if (chain->bytes != focus->bytes) { /* XXX what if compressed? */ nradix = hammer2_getradix(chain->bytes); - hammer2_chain_resize(NULL, parent, chain, - mtid, 0, - nradix, 0); + hammer2_chain_resize(chain, mtid, 0, nradix, 0); } hammer2_chain_modify(chain, mtid, 0, 0); otype = chain->bref.type; @@ -1009,6 +1007,18 @@ hammer2_sync_replace(hammer2_thread_t *thr, bcopy(focus->data, chain->data, chain->bytes); hammer2_chain_setcheck(chain, chain->data); break; + case HAMMER2_BREF_TYPE_DIRENT: + /* + * Directory entries embed data in the blockref. + */ + if (chain->bytes) { + bcopy(focus->data, chain->data, chain->bytes); + hammer2_chain_setcheck(chain, chain->data); + } else { + chain->bref.check = focus->bref.check; + } + chain->bref.embed = focus->bref.embed; + break; default: KKASSERT(0); break; diff --git a/sys/vfs/hammer2/hammer2_vfsops.c b/sys/vfs/hammer2/hammer2_vfsops.c index 1389fae2b2..ee6de4e6f9 100644 --- a/sys/vfs/hammer2/hammer2_vfsops.c +++ b/sys/vfs/hammer2/hammer2_vfsops.c @@ -2028,6 +2028,7 @@ hammer2_recovery_scan(hammer2_dev_t *hmp, hammer2_chain_t *parent, hammer2_chain_lock(parent, HAMMER2_RESOLVE_ALWAYS); hammer2_chain_unlock(parent); break; + case HAMMER2_BREF_TYPE_DIRENT: case HAMMER2_BREF_TYPE_DATA: case HAMMER2_BREF_TYPE_FREEMAP: case HAMMER2_BREF_TYPE_FREEMAP_NODE: diff --git a/sys/vfs/hammer2/hammer2_vnops.c b/sys/vfs/hammer2/hammer2_vnops.c index 5dcfc85f7a..04283bef83 100644 --- a/sys/vfs/hammer2/hammer2_vnops.c +++ b/sys/vfs/hammer2/hammer2_vnops.c @@ -511,7 +511,6 @@ hammer2_vop_readdir(struct vop_readdir_args *ap) int ncookies; int error; int eofflag; - int dtype; int r; LOCKSTART; @@ -597,6 +596,8 @@ hammer2_vop_readdir(struct vop_readdir_args *ap) for (;;) { const hammer2_inode_data_t *ripdata; + const char *dname; + int dtype; error = hammer2_xop_collect(&xop->head, 0); if (error) @@ -608,10 +609,12 @@ hammer2_vop_readdir(struct vop_readdir_args *ap) xop->head.cluster.focus, (xop->head.cluster.focus ? xop->head.cluster.focus->data : (void *)-1)); - ripdata = &hammer2_cluster_rdata(&xop->head.cluster)->ipdata; hammer2_cluster_bref(&xop->head.cluster, &bref); + if (bref.type == HAMMER2_BREF_TYPE_INODE) { - dtype = hammer2_get_dtype(ripdata); + ripdata = + &hammer2_cluster_rdata(&xop->head.cluster)->ipdata; + dtype = hammer2_get_dtype(ripdata->meta.type); saveoff = bref.key & HAMMER2_DIRHASH_USERMSK; r = vop_write_dirent(&error, uio, ripdata->meta.inum & @@ -624,6 +627,21 @@ hammer2_vop_readdir(struct vop_readdir_args *ap) if (cookies) cookies[cookie_index] = saveoff; ++cookie_index; + } else if (bref.type == HAMMER2_BREF_TYPE_DIRENT) { + dtype = hammer2_get_dtype(bref.embed.dirent.type); + saveoff = bref.key & HAMMER2_DIRHASH_USERMSK; + if (bref.embed.dirent.namlen <= + sizeof(bref.check.buf)) { + dname = bref.check.buf; + } else { + dname = + hammer2_cluster_rdata(&xop->head.cluster)->buf; + } + r = vop_write_dirent(&error, uio, + bref.embed.dirent.inum, + dtype, + bref.embed.dirent.namlen, + dname); } else { /* XXX chain error */ kprintf("bad chain type readdir %d\n", bref.type); @@ -1297,20 +1315,16 @@ hammer2_vop_nmkdir(struct vop_nmkdir_args *ap) /* * Create the actual inode as a hidden file in the iroot, then - * create the directory entry as a hardlink to it. The creation - * of the actual inode sets its nlinks to 1 which is the value - * we desire. + * create the directory entry. The creation of the actual inode + * sets its nlinks to 1 which is the value we desire. */ nip = hammer2_inode_create(dip->pmp->iroot, dip, ap->a_vap, ap->a_cred, NULL, 0, inum, inum, 0, 0, 0, &error); if (error == 0) { - hammer2_inode_create(dip, dip, NULL, NULL, - name, name_len, 0, - nip->meta.inum, - HAMMER2_OBJTYPE_HARDLINK, nip->meta.type, - 0, &error); + error = hammer2_dirent_create(dip, name, name_len, + nip->meta.inum, nip->meta.type); } if (error) { @@ -1434,14 +1448,11 @@ hammer2_vop_nlink(struct vop_nlink_args *ap) hammer2_inode_lock(ip, 0); /* - * Create the hardlink target and bump nlinks. + * Create the directory entry and bump nlinks. */ if (error == 0) { - hammer2_inode_create(tdip, tdip, NULL, NULL, - name, name_len, 0, - ip->meta.inum, - HAMMER2_OBJTYPE_HARDLINK, ip->meta.type, - 0, &error); + error = hammer2_dirent_create(tdip, name, name_len, + ip->meta.inum, ip->meta.type); hammer2_inode_modify(ip); ++ip->meta.nlinks; } @@ -1502,9 +1513,8 @@ hammer2_vop_ncreate(struct vop_ncreate_args *ap) /* * Create the actual inode as a hidden file in the iroot, then - * create the directory entry as a hardlink to it. The creation - * of the actual inode sets its nlinks to 1 which is the value - * we desire. + * create the directory entry. The creation of the actual inode + * sets its nlinks to 1 which is the value we desire. */ nip = hammer2_inode_create(dip->pmp->iroot, dip, ap->a_vap, ap->a_cred, NULL, 0, inum, @@ -1512,11 +1522,8 @@ hammer2_vop_ncreate(struct vop_ncreate_args *ap) 0, &error); if (error == 0) { - hammer2_inode_create(dip, dip, NULL, NULL, - name, name_len, 0, - nip->meta.inum, - HAMMER2_OBJTYPE_HARDLINK, nip->meta.type, - 0, &error); + error = hammer2_dirent_create(dip, name, name_len, + nip->meta.inum, nip->meta.type); } if (error) { KKASSERT(nip == NULL); @@ -1578,10 +1585,7 @@ hammer2_vop_nmknod(struct vop_nmknod_args *ap) hammer2_trans_init(dip->pmp, 0); /* - * The device node is entered as the directory entry itself and not - * as a hardlink to an inode. Since one cannot obtain a - * file handle on the filesystem entry representing the device, we - * do not have to worry about indexing its inode. + * Create the device inode and then create the directory entry. */ inum = hammer2_trans_newinum(dip->pmp); nip = hammer2_inode_create(dip->pmp->iroot, dip, ap->a_vap, ap->a_cred, @@ -1589,11 +1593,8 @@ hammer2_vop_nmknod(struct vop_nmknod_args *ap) inum, 0, 0, 0, &error); if (error == 0) { - hammer2_inode_create(dip, dip, NULL, NULL, - name, name_len, 0, - nip->meta.inum, - HAMMER2_OBJTYPE_HARDLINK, nip->meta.type, - 0, &error); + error = hammer2_dirent_create(dip, name, name_len, + nip->meta.inum, nip->meta.type); } @@ -1656,10 +1657,8 @@ hammer2_vop_nsymlink(struct vop_nsymlink_args *ap) ap->a_vap->va_type = VLNK; /* enforce type */ /* - * The softlink is entered into the directory itself and not - * as a hardlink to an inode. Since one cannot obtain a - * file handle on the softlink itself we do not have to worry - * about indexing its inode. + * Create the softlink as an inode and then create the directory + * entry. */ inum = hammer2_trans_newinum(dip->pmp); @@ -1668,11 +1667,8 @@ hammer2_vop_nsymlink(struct vop_nsymlink_args *ap) inum, 0, 0, 0, &error); if (error == 0) { - hammer2_inode_create(dip, dip, NULL, NULL, - name, name_len, 0, - nip->meta.inum, - HAMMER2_OBJTYPE_HARDLINK, nip->meta.type, - 0, &error); + error = hammer2_dirent_create(dip, name, name_len, + nip->meta.inum, nip->meta.type); } diff --git a/sys/vfs/hammer2/hammer2_xops.c b/sys/vfs/hammer2/hammer2_xops.c index 19a182c523..d46f808b67 100644 --- a/sys/vfs/hammer2/hammer2_xops.c +++ b/sys/vfs/hammer2/hammer2_xops.c @@ -72,19 +72,21 @@ checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex) hammer2_chain_t *parent; hammer2_chain_t *chain; hammer2_key_t key_next; + hammer2_key_t inum; int cache_index = -1; int error; error = 0; chain = hammer2_chain_lookup_init(ochain, 0); - if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { + if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { if (oparent) hammer2_chain_unlock(oparent); - + inum = chain->bref.embed.dirent.inum; parent = NULL; - error = hammer2_chain_hardlink_find(&parent, &chain, - clindex, 0); + error = hammer2_chain_inode_find(chain->pmp, inum, + clindex, 0, + &parent, &chain); if (parent) { hammer2_chain_unlock(parent); hammer2_chain_drop(parent); @@ -103,6 +105,10 @@ checkdirempty(hammer2_chain_t *oparent, hammer2_chain_t *ochain, int clindex) } } + /* + * Determine if the directory is empty or not by checking its + * visible namespace (the area which contains directory entries). + */ parent = chain; chain = NULL; if (parent) { @@ -221,7 +227,6 @@ hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg) hammer2_xop_nresolve_t *xop = &arg->xop_nresolve; hammer2_chain_t *parent; hammer2_chain_t *chain; - const hammer2_inode_data_t *ripdata; const char *name; size_t name_len; hammer2_key_t key_next; @@ -251,12 +256,8 @@ hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg) HAMMER2_LOOKUP_ALWAYS | HAMMER2_LOOKUP_SHARED); while (chain) { - ripdata = &chain->data->ipdata; - if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && - ripdata->meta.name_len == name_len && - bcmp(ripdata->filename, name, name_len) == 0) { + if (hammer2_chain_dirent_test(chain, name, name_len)) break; - } chain = hammer2_chain_next(&parent, chain, &key_next, key_next, lhc + HAMMER2_DIRHASH_LOMASK, @@ -270,10 +271,14 @@ hammer2_xop_nresolve(hammer2_thread_t *thr, hammer2_xop_t *arg) */ error = 0; if (chain) { - if (chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { - error = hammer2_chain_hardlink_find(&parent, &chain, - thr->clindex, - HAMMER2_LOOKUP_SHARED); + if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { + lhc = chain->bref.embed.dirent.inum; + error = hammer2_chain_inode_find(chain->pmp, + lhc, + thr->clindex, + HAMMER2_LOOKUP_SHARED, + &parent, + &chain); } } done: @@ -292,19 +297,17 @@ done: * Backend for hammer2_vop_nremove(), hammer2_vop_nrmdir(), helper * for hammer2_vop_nrename(), and backend for pfs_delete. * - * This function locates and removes a directory entry. If the entry is - * a hardlink pointer, this function does NOT remove the hardlink target, - * but will lookup and return the hardlink target. - * - * Note that any hardlink target's nlinks may not be synchronized to the - * in-memory inode. hammer2_inode_unlink_finisher() is responsible for the - * final disposition of the hardlink target. + * This function locates and removes a directory entry, and will lookup + * and return the underlying inode. For directory entries the underlying + * inode is not removed. If the directory entry is the actual inode itself, + * it may be conditonally removed and returned. * - * If an inode pointer we lookup and return the actual inode. If not, we - * return the deleted directory entry. + * WARNING! Any target inode's nlinks may not be synchronized to the + * in-memory inode. hammer2_inode_unlink_finisher() is + * responsible for the final disposition of the actual inode. * - * The frontend is responsible for moving open inodes to the hidden directory - * and for decrementing nlinks. + * The frontend is responsible for moving open-but-deleted inodes to the + * mount's hidden directory and for decrementing nlinks. */ void hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg) @@ -312,7 +315,6 @@ hammer2_xop_unlink(hammer2_thread_t *thr, hammer2_xop_t *arg) hammer2_xop_unlink_t *xop = &arg->xop_unlink; hammer2_chain_t *parent; hammer2_chain_t *chain; - const hammer2_inode_data_t *ripdata; const char *name; size_t name_len; hammer2_key_t key_next; @@ -344,12 +346,8 @@ again: &cache_index, HAMMER2_LOOKUP_ALWAYS); while (chain) { - ripdata = &chain->data->ipdata; - if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && - ripdata->meta.name_len == name_len && - bcmp(ripdata->filename, name, name_len) == 0) { + if (hammer2_chain_dirent_test(chain, name, name_len)) break; - } chain = hammer2_chain_next(&parent, chain, &key_next, key_next, lhc + HAMMER2_DIRHASH_LOMASK, @@ -358,8 +356,10 @@ again: } /* - * The directory entry will almost always be a hardlink pointer, - * which we permanently delete. Otherwise we go by xop->dopermanent. + * The directory entry will either be a BREF_TYPE_DIRENT or a + * BREF_TYPE_INODE. We always permanently delete DIRENTs, but + * must go by xop->dopermanent for BREF_TYPE_INODE. + * * Note that the target chain's nlinks may not be synchronized with * the in-memory hammer2_inode_t structure, so we don't try to do * anything fancy here. @@ -373,16 +373,16 @@ again: /* * If the directory entry is the actual inode then use its * type for the directory typing tests, otherwise if it is - * a hardlink pointer then use the secondary type field for - * directory typing tests. + * a directory entry, pull the type field from the entry. * - * Also, hardlink pointers are always permanently deleted + * Directory entries are always permanently deleted * (because they aren't the actual inode). */ - type = chain->data->ipdata.meta.type; - if (type == HAMMER2_OBJTYPE_HARDLINK) { - type = chain->data->ipdata.meta.target_type; + if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { + type = chain->bref.embed.dirent.type; dopermanent |= HAMMER2_DELETE_PERMANENT; + } else { + type = chain->data->ipdata.meta.type; } /* @@ -422,9 +422,8 @@ again: error = EISDIR; } else { /* - * This deletes the directory entry itself, which is - * also the inode when nlinks == 1. Hardlink targets - * are handled in the next conditional. + * Delete the directory entry. chain might also + * be a directly-embedded inode. */ error = chain->error; hammer2_chain_delete(parent, chain, @@ -433,22 +432,21 @@ again: } /* - * If the entry is a hardlink pointer, resolve it. We do not try - * to manipulate the contents of the hardlink target as it might - * not be synchronized with the front-end hammer2_inode_t. Nor do - * we try to lookup the front-end hammer2_inode_t here (we are the - * backend!). + * If chain is a directory entry we must resolve it. We do not try + * to manipulate the contents as it might not be synchronized with + * the frontend hammer2_inode_t, nor do we try to lookup the + * frontend hammer2_inode_t here (we are the backend!). */ - if (chain && - chain->data->ipdata.meta.type == HAMMER2_OBJTYPE_HARDLINK) { + if (chain && chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { int error2; - lhc = chain->data->ipdata.meta.inum; + lhc = chain->bref.embed.dirent.inum; - error2 = hammer2_chain_hardlink_find(&parent, &chain, - thr->clindex, 0); + error2 = hammer2_chain_inode_find(chain->pmp, lhc, + thr->clindex, 0, + &parent, &chain); if (error2) { - kprintf("hardlink_find: %016jx %p failed\n", + kprintf("inode_find: %016jx %p failed\n", lhc, chain); error2 = 0; /* silently ignore */ } @@ -478,7 +476,7 @@ done: * Backend for hammer2_vop_nrename() * * This handles the final step of renaming, either renaming the - * actual inode or renaming the hardlink pointer. + * actual inode or renaming the directory entry. */ void hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) @@ -496,8 +494,8 @@ hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) /* * We need the precise parent chain to issue the deletion. * - * If this is not a hardlink target we can act on the inode, - * otherwise we have to locate the hardlink pointer. + * If this is a directory entry we must locate the underlying + * inode. If it is an embedded inode we can act directly on it. */ ip = xop->head.ip2; pmp = ip->pmp; @@ -524,10 +522,9 @@ hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) } } else { /* - * The hardlink pointer for the head.ip1 hardlink target + * The directory entry for the head.ip1 inode * is in fdip, do a namespace search. */ - const hammer2_inode_data_t *ripdata; hammer2_key_t lhc; hammer2_key_t key_next; const char *name; @@ -552,12 +549,8 @@ hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) &cache_index, HAMMER2_LOOKUP_ALWAYS); while (chain) { - ripdata = &chain->data->ipdata; - if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && - ripdata->meta.name_len == name_len && - bcmp(ripdata->filename, name, name_len) == 0) { + if (hammer2_chain_dirent_test(chain, name, name_len)) break; - } chain = hammer2_chain_next(&parent, chain, &key_next, key_next, lhc + HAMMER2_DIRHASH_LOMASK, @@ -585,31 +578,56 @@ hammer2_xop_nrename(hammer2_thread_t *thr, hammer2_xop_t *arg) /* * Ok, back to the deleted chain. We must reconnect this chain - * to tdir (ip3). The chain (a real inode or a hardlink pointer) - * is not otherwise modified. - * - * Frontend is expected to replicate the same inode meta data - * modifications. + * to tdir (ip3) and adjust the filename. The chain (a real inode + * or a directory entry) is not otherwise modified. * - * NOTE! This chain may not represent the actual inode, it - * can be a hardlink pointer. - * - * XXX in-inode parent directory specification? + * The frontend is expected to replicate the same inode meta data + * modifications if necessary. */ - if (chain->data->ipdata.meta.name_key != xop->lhc || + if (chain->bref.key != xop->lhc || xop->head.name1_len != xop->head.name2_len || bcmp(xop->head.name1, xop->head.name2, xop->head.name1_len) != 0) { - hammer2_inode_data_t *wipdata; + if (chain->bref.type == HAMMER2_BREF_TYPE_INODE) { + hammer2_inode_data_t *wipdata; - hammer2_chain_modify(chain, xop->head.mtid, 0, 0); - wipdata = &chain->data->ipdata; + hammer2_chain_modify(chain, xop->head.mtid, 0, 0); + wipdata = &chain->data->ipdata; - bzero(wipdata->filename, sizeof(wipdata->filename)); - bcopy(xop->head.name2, wipdata->filename, xop->head.name2_len); - wipdata->meta.name_key = xop->lhc; - wipdata->meta.name_len = xop->head.name2_len; + bzero(wipdata->filename, sizeof(wipdata->filename)); + bcopy(xop->head.name2, wipdata->filename, + xop->head.name2_len); + wipdata->meta.name_key = xop->lhc; + wipdata->meta.name_len = xop->head.name2_len; + } + if (chain->bref.type == HAMMER2_BREF_TYPE_DIRENT) { + if (xop->head.name2_len <= sizeof(chain->bref.check.buf)) { + hammer2_chain_resize(chain, xop->head.mtid, 0, + 0, 0); + hammer2_chain_modify(chain, xop->head.mtid, + 0, 0); + bzero(chain->bref.check.buf, + sizeof(chain->bref.check.buf)); + bcopy(xop->head.name2, chain->bref.check.buf, + xop->head.name2_len); + } else { + hammer2_chain_resize(chain, xop->head.mtid, 0, + hammer2_getradix(HAMMER2_ALLOC_MIN), 0); + hammer2_chain_modify(chain, xop->head.mtid, + 0, 0); + bzero(chain->data->buf, + sizeof(chain->data->buf)); + bcopy(xop->head.name2, chain->data->buf, + xop->head.name2_len); + } + chain->bref.embed.dirent.namlen = xop->head.name2_len; + } } - if (chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) { + + /* + * If an embedded inode, adjust iparent directly. + */ + if (chain->bref.type == HAMMER2_BREF_TYPE_INODE && + chain->data->ipdata.meta.iparent != xop->head.ip3->meta.inum) { hammer2_inode_data_t *wipdata; hammer2_chain_modify(chain, xop->head.mtid, 0, 0); -- 2.41.0