From 4c286c362d37391fa082d7eb6975b4519ff49d91 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Wed, 29 Apr 2009 15:34:59 -0700 Subject: [PATCH] HAMMER VFS - Better CRC handling, bad-file handling. Data CRC errors should now generate EIO instead of panic()ing the system. B-Tree CRC errors might still panic() and freemap CRC errors WILL still panic(). Continuing from DDB on a B-Tree node CRC error when debugging is enabled now no longer marks the B-Tree node as good. The mirror-read command will now transfer data records with bad CRCs instead of aborting the transfer, identifying them with a new type field. The mirror-write ioctl currently ignores such records. If a directory entry is encountered and the related inode cannot be looked up, generate a dummy in-memory inode of type FIFO to placemark the bad directory entry, allowing it to be removed. Currently it is possible for a directory entry to be synced to the media in a different transaction then the related inode (a bug which needs to be fixed). If a crash occurs at the wrong time the recovery code can leave the media in a state where the directory entry exists but the inode does not. This change allows the bad directory entry to be removed. Reported-by: Antonio Huete Jimenez --- sys/vfs/hammer/hammer.h | 14 +++- sys/vfs/hammer/hammer_btree.c | 7 +- sys/vfs/hammer/hammer_cursor.c | 2 +- sys/vfs/hammer/hammer_inode.c | 123 +++++++++++++++++++++++++++++++-- sys/vfs/hammer/hammer_ioctl.h | 17 +++++ sys/vfs/hammer/hammer_mirror.c | 31 +++++++-- sys/vfs/hammer/hammer_object.c | 35 +++++++--- sys/vfs/hammer/hammer_ondisk.c | 48 +++++++++---- sys/vfs/hammer/hammer_vnops.c | 35 ++++++++-- 9 files changed, 271 insertions(+), 41 deletions(-) diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h index 415a4b5393..04d4e94a95 100644 --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -118,6 +118,7 @@ typedef struct hammer_transaction *hammer_transaction_t; #define HAMMER_TRANSF_NEWINODE 0x0001 #define HAMMER_TRANSF_DIDIO 0x0002 +#define HAMMER_TRANSF_CRCDOM 0x0004 /* EDOM on CRC error, less critical */ /* * HAMMER locks @@ -337,6 +338,7 @@ typedef struct hammer_inode *hammer_inode_t; #define HAMMER_INODE_ATIME 0x00100000 /* in-memory atime modified */ #define HAMMER_INODE_MTIME 0x00200000 /* in-memory mtime modified */ #define HAMMER_INODE_WOULDBLOCK 0x00400000 /* re-issue to new flush group */ +#define HAMMER_INODE_DUMMY 0x00800000 /* dummy inode covering bad file */ #define HAMMER_INODE_MODMASK (HAMMER_INODE_DDIRTY| \ HAMMER_INODE_XDIRTY|HAMMER_INODE_BUFS| \ @@ -587,6 +589,9 @@ struct hammer_node { #define HAMMER_NODE_CRCGOOD 0x0004 #define HAMMER_NODE_NEEDSCRC 0x0008 #define HAMMER_NODE_NEEDSMIRROR 0x0010 +#define HAMMER_NODE_CRCBAD 0x0020 + +#define HAMMER_NODE_CRCANY (HAMMER_NODE_CRCGOOD | HAMMER_NODE_CRCBAD) typedef struct hammer_node *hammer_node_t; @@ -853,6 +858,10 @@ struct hammer_inode *hammer_get_inode(hammer_transaction_t trans, hammer_inode_t dip, int64_t obj_id, hammer_tid_t asof, u_int32_t localization, int flags, int *errorp); +struct hammer_inode *hammer_get_dummy_inode(hammer_transaction_t trans, + hammer_inode_t dip, int64_t obj_id, + hammer_tid_t asof, u_int32_t localization, + int flags, int *errorp); void hammer_scan_inode_snapshots(hammer_mount_t hmp, hammer_inode_info_t iinfo, int (*callback)(hammer_inode_t ip, void *data), @@ -1028,7 +1037,7 @@ int hammer_vfs_export(struct mount *mp, int op, hammer_node_t hammer_get_node(hammer_transaction_t trans, hammer_off_t node_offset, int isnew, int *errorp); void hammer_ref_node(hammer_node_t node); -hammer_node_t hammer_ref_node_safe(struct hammer_mount *hmp, +hammer_node_t hammer_ref_node_safe(hammer_transaction_t trans, hammer_node_cache_t cache, int *errorp); void hammer_rel_node(hammer_node_t node); void hammer_delete_node(hammer_transaction_t trans, @@ -1245,12 +1254,14 @@ hammer_lock_ex(struct hammer_lock *lock) static __inline void hammer_modify_node_noundo(hammer_transaction_t trans, hammer_node_t node) { + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, NULL, 0); } static __inline void hammer_modify_node_all(hammer_transaction_t trans, struct hammer_node *node) { + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, node->ondisk, sizeof(*node->ondisk)); } @@ -1264,6 +1275,7 @@ hammer_modify_node(hammer_transaction_t trans, hammer_node_t node, KKASSERT((char *)base >= (char *)node->ondisk && (char *)base + len <= (char *)node->ondisk + sizeof(*node->ondisk)); + KKASSERT((node->flags & HAMMER_NODE_CRCBAD) == 0); hammer_modify_buffer(trans, node->buffer, base, len); crcptr = &node->ondisk->crc; hammer_modify_buffer(trans, node->buffer, crcptr, sizeof(hammer_crc_t)); diff --git a/sys/vfs/hammer/hammer_btree.c b/sys/vfs/hammer/hammer_btree.c index e51f7f7b19..56a4b34459 100644 --- a/sys/vfs/hammer/hammer_btree.c +++ b/sys/vfs/hammer/hammer_btree.c @@ -710,7 +710,12 @@ hammer_btree_extract(hammer_cursor_t cursor, int flags) if (hammer_crc_test_leaf(cursor->data, &elm->leaf) == 0) { kprintf("CRC DATA @ %016llx/%d FAILED\n", elm->leaf.data_offset, elm->leaf.data_len); - Debugger("CRC FAILED: DATA"); + if (hammer_debug_debug & 0x0001) + Debugger("CRC FAILED: DATA"); + if (cursor->trans->flags & HAMMER_TRANSF_CRCDOM) + error = EDOM; /* less critical (mirroring) */ + else + error = EIO; /* critical */ } return(error); } diff --git a/sys/vfs/hammer/hammer_cursor.c b/sys/vfs/hammer/hammer_cursor.c index 7b340bf402..679eceb528 100644 --- a/sys/vfs/hammer/hammer_cursor.c +++ b/sys/vfs/hammer/hammer_cursor.c @@ -73,7 +73,7 @@ hammer_init_cursor(hammer_transaction_t trans, hammer_cursor_t cursor, * Step 1 - acquire a locked node from the cache if possible */ if (cache && cache->node) { - node = hammer_ref_node_safe(trans->hmp, cache, &error); + node = hammer_ref_node_safe(trans, cache, &error); if (error == 0) { hammer_lock_sh(&node->lock); if (node->flags & HAMMER_NODE_DELETED) { diff --git a/sys/vfs/hammer/hammer_inode.c b/sys/vfs/hammer/hammer_inode.c index 73c75de724..56aec8c862 100644 --- a/sys/vfs/hammer/hammer_inode.c +++ b/sys/vfs/hammer/hammer_inode.c @@ -365,6 +365,11 @@ hammer_get_inode(hammer_transaction_t trans, hammer_inode_t dip, * of inodes. Otherwise we can continue to * add new inodes * faster then they can be disposed of, even with the tsleep * delay. + * + * If we find a dummy inode we return a failure so dounlink + * (which does another lookup) doesn't try to mess with the + * link count. hammer_vop_nresolve() uses hammer_get_dummy_inode() + * to ref dummy inodes. */ iinfo.obj_id = obj_id; iinfo.obj_asof = asof; @@ -372,10 +377,10 @@ hammer_get_inode(hammer_transaction_t trans, hammer_inode_t dip, loop: ip = hammer_ino_rb_tree_RB_LOOKUP_INFO(&hmp->rb_inos_root, &iinfo); if (ip) { -#if 0 - if (ip->vp == NULL) - trans->flags |= HAMMER_TRANSF_NEWINODE; -#endif + if (ip->flags & HAMMER_INODE_DUMMY) { + *errorp = ENOENT; + return(NULL); + } hammer_ref(&ip->lock); *errorp = 0; return(ip); @@ -499,6 +504,114 @@ retry: return (ip); } +/* + * Get a dummy inode to placemark a broken directory entry. + */ +struct hammer_inode * +hammer_get_dummy_inode(hammer_transaction_t trans, hammer_inode_t dip, + int64_t obj_id, hammer_tid_t asof, u_int32_t localization, + int flags, int *errorp) +{ + hammer_mount_t hmp = trans->hmp; + struct hammer_inode_info iinfo; + struct hammer_inode *ip; + + /* + * Determine if we already have an inode cached. If we do then + * we are golden. + * + * If we find an inode with no vnode we have to mark the + * transaction such that hammer_inode_waitreclaims() is + * called later on to avoid building up an infinite number + * of inodes. Otherwise we can continue to * add new inodes + * faster then they can be disposed of, even with the tsleep + * delay. + * + * If we find a non-fake inode we return an error. Only fake + * inodes can be returned by this routine. + */ + iinfo.obj_id = obj_id; + iinfo.obj_asof = asof; + iinfo.obj_localization = localization; +loop: + *errorp = 0; + ip = hammer_ino_rb_tree_RB_LOOKUP_INFO(&hmp->rb_inos_root, &iinfo); + if (ip) { + if ((ip->flags & HAMMER_INODE_DUMMY) == 0) { + *errorp = ENOENT; + return(NULL); + } + hammer_ref(&ip->lock); + return(ip); + } + + /* + * Allocate a new inode structure and deal with races later. + */ + ip = kmalloc(sizeof(*ip), hmp->m_inodes, M_WAITOK|M_ZERO); + ++hammer_count_inodes; + ++hmp->count_inodes; + ip->obj_id = obj_id; + ip->obj_asof = iinfo.obj_asof; + ip->obj_localization = localization; + ip->hmp = hmp; + ip->flags = flags | HAMMER_INODE_RO | HAMMER_INODE_DUMMY; + ip->cache[0].ip = ip; + ip->cache[1].ip = ip; + ip->sync_trunc_off = ip->trunc_off = ip->save_trunc_off = + 0x7FFFFFFFFFFFFFFFLL; + RB_INIT(&ip->rec_tree); + TAILQ_INIT(&ip->target_list); + hammer_ref(&ip->lock); + + /* + * Populate the dummy inode. Leave everything zero'd out. + * + * (ip->ino_leaf and ip->ino_data) + * + * Make the dummy inode a FIFO object which most copy programs + * will properly ignore. + */ + ip->save_trunc_off = ip->ino_data.size; + ip->ino_data.obj_type = HAMMER_OBJTYPE_FIFO; + + /* + * Locate and assign the pseudofs management structure to + * the inode. + */ + if (dip && dip->obj_localization == ip->obj_localization) { + ip->pfsm = dip->pfsm; + hammer_ref(&ip->pfsm->lock); + } else { + ip->pfsm = hammer_load_pseudofs(trans, ip->obj_localization, + errorp); + *errorp = 0; /* ignore ENOENT */ + } + + /* + * The inode is placed on the red-black tree and will be synced to + * the media when flushed or by the filesystem sync. If this races + * another instantiation/lookup the insertion will fail. + * + * NOTE: Do not set HAMMER_INODE_ONDISK. The inode is a fake. + */ + if (*errorp == 0) { + if (RB_INSERT(hammer_ino_rb_tree, &hmp->rb_inos_root, ip)) { + hammer_free_inode(ip); + goto loop; + } + } else { + if (ip->flags & HAMMER_INODE_RSV_INODES) { + ip->flags &= ~HAMMER_INODE_RSV_INODES; /* sanity */ + --hmp->rsv_inodes; + } + hammer_free_inode(ip); + ip = NULL; + } + trans->flags |= HAMMER_TRANSF_NEWINODE; + return (ip); +} + /* * Create a new filesystem object, returning the inode in *ipp. The * returned inode will be referenced. The inode is created in-memory. @@ -2508,7 +2621,7 @@ hammer_sync_inode(hammer_transaction_t trans, hammer_inode_t ip) * out from under us. */ if (error == 0) { - tmp_node = hammer_ref_node_safe(ip->hmp, &ip->cache[0], &error); + tmp_node = hammer_ref_node_safe(trans, &ip->cache[0], &error); if (tmp_node) { hammer_cursor_downgrade(&cursor); hammer_lock_sh(&tmp_node->lock); diff --git a/sys/vfs/hammer/hammer_ioctl.h b/sys/vfs/hammer/hammer_ioctl.h index 73ab0912bb..46eaa8f4be 100644 --- a/sys/vfs/hammer/hammer_ioctl.h +++ b/sys/vfs/hammer/hammer_ioctl.h @@ -324,6 +324,13 @@ union hammer_ioc_mrecord_any { typedef union hammer_ioc_mrecord_any *hammer_ioc_mrecord_any_t; +/* + * MREC types. Flags are in the upper 16 bits but some are also included + * in the type mask to force them into any switch() on the type. + * + * NOTE: Any record whos data is CRC-errored will have HAMMER_MRECF_CRC set, + * and the bit is also part of the type mask. + */ #define HAMMER_MREC_TYPE_RESERVED 0 #define HAMMER_MREC_TYPE_REC 1 /* record w/ data */ #define HAMMER_MREC_TYPE_PFSD 2 /* (userland only) */ @@ -334,6 +341,16 @@ typedef union hammer_ioc_mrecord_any *hammer_ioc_mrecord_any_t; #define HAMMER_MREC_TYPE_TERM 7 /* (userland only) */ #define HAMMER_MREC_TYPE_IDLE 8 /* (userland only) */ +#define HAMMER_MREC_TYPE_REC_BADCRC (HAMMER_MREC_TYPE_REC | \ + HAMMER_MRECF_CRC_ERROR) + +#define HAMMER_MRECF_TYPE_LOMASK 0x000000FF +#define HAMMER_MRECF_TYPE_MASK 0x800000FF +#define HAMMER_MRECF_CRC_ERROR 0x80000000 + +#define HAMMER_MRECF_DATA_CRC_BAD 0x40000000 +#define HAMMER_MRECF_RECD_CRC_BAD 0x20000000 + #define HAMMER_MREC_CRCOFF (offsetof(struct hammer_ioc_mrecord_head, rec_size)) #define HAMMER_MREC_HEADSIZE sizeof(struct hammer_ioc_mrecord_head) diff --git a/sys/vfs/hammer/hammer_mirror.c b/sys/vfs/hammer/hammer_mirror.c index 6eaf126199..2267902fde 100644 --- a/sys/vfs/hammer/hammer_mirror.c +++ b/sys/vfs/hammer/hammer_mirror.c @@ -89,6 +89,7 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip, int data_len; int bytes; int eatdisk; + int mrec_flags; u_int32_t localization; u_int32_t rec_crc; @@ -107,6 +108,12 @@ hammer_ioc_mirror_read(hammer_transaction_t trans, hammer_inode_t ip, bzero(&mrec, sizeof(mrec)); bzero(&cmirror, sizeof(cmirror)); + /* + * Make CRC errors non-fatal (at least on data), causing an EDOM + * error instead of EIO. + */ + trans->flags |= HAMMER_TRANSF_CRCDOM; + retry: error = hammer_init_cursor(trans, &cursor, NULL, NULL); if (error) { @@ -224,13 +231,21 @@ retry: /* * The core code exports the data to userland. + * + * CRC errors on data are reported but passed through, + * but the data must be washed by the user program. */ + mrec_flags = 0; data_len = (elm->data_offset) ? elm->data_len : 0; if (data_len) { error = hammer_btree_extract(&cursor, HAMMER_CURSOR_GET_DATA); - if (error) - break; + if (error) { + if (error != EDOM) + break; + mrec_flags |= HAMMER_MRECF_CRC_ERROR | + HAMMER_MRECF_DATA_CRC_BAD; + } } bytes = sizeof(mrec.rec) + data_len; @@ -246,9 +261,10 @@ retry: * userland and delete_tid is cleared. */ mrec.head.signature = HAMMER_IOC_MIRROR_SIGNATURE; - mrec.head.type = HAMMER_MREC_TYPE_REC; + mrec.head.type = HAMMER_MREC_TYPE_REC | mrec_flags; mrec.head.rec_size = bytes; mrec.rec.leaf = *elm; + if (elm->base.delete_tid > mirror->tid_end) mrec.rec.leaf.base.delete_tid = 0; rec_crc = crc32(&mrec.head.rec_size, @@ -401,7 +417,7 @@ hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip, break; } - switch(mrec.head.type) { + switch(mrec.head.type & HAMMER_MRECF_TYPE_MASK) { case HAMMER_MREC_TYPE_SKIP: if (mrec.head.rec_size != sizeof(mrec.skip)) error = EINVAL; @@ -414,6 +430,13 @@ hammer_ioc_mirror_write(hammer_transaction_t trans, hammer_inode_t ip, if (error == 0) error = hammer_ioc_mirror_write_rec(&cursor, &mrec.rec, mirror, localization, uptr + sizeof(mrec.rec)); break; + case HAMMER_MREC_TYPE_REC_BADCRC: + /* + * Records with bad data payloads are ignored XXX. + */ + if (mrec.head.rec_size < sizeof(mrec.rec)) + error = EINVAL; + break; case HAMMER_MREC_TYPE_PASS: if (mrec.head.rec_size != sizeof(mrec.rec)) error = EINVAL; diff --git a/sys/vfs/hammer/hammer_object.c b/sys/vfs/hammer/hammer_object.c index aca7a2929e..ee30f33011 100644 --- a/sys/vfs/hammer/hammer_object.c +++ b/sys/vfs/hammer/hammer_object.c @@ -703,7 +703,8 @@ failed: * cursor must be seeked to the directory entry record being deleted. * * The related inode should be share-locked by the caller. The caller is - * on the frontend. + * on the frontend. It could also be NULL indicating that the directory + * entry being removed has no related inode. * * This function can return EDEADLK requiring the caller to terminate * the cursor, any locks, wait on the returned record, and retry. @@ -749,9 +750,18 @@ hammer_ip_del_directory(struct hammer_transaction *trans, record->type = HAMMER_MEM_RECORD_DEL; record->leaf.base = cursor->leaf->base; + /* + * ip may be NULL, indicating the deletion of a directory + * entry which has no related inode. + */ record->target_ip = ip; - record->flush_state = HAMMER_FST_SETUP; - TAILQ_INSERT_TAIL(&ip->target_list, record, target_entry); + if (ip) { + record->flush_state = HAMMER_FST_SETUP; + TAILQ_INSERT_TAIL(&ip->target_list, record, + target_entry); + } else { + record->flush_state = HAMMER_FST_IDLE; + } /* * The inode now has a dependancy and must be taken out of @@ -762,7 +772,7 @@ hammer_ip_del_directory(struct hammer_transaction *trans, * reflush when the dependancies are disposed of if someone * is waiting on the inode. */ - if (ip->flush_state == HAMMER_FST_IDLE) { + if (ip && ip->flush_state == HAMMER_FST_IDLE) { hammer_ref(&ip->lock); ip->flush_state = HAMMER_FST_SETUP; if (ip->flags & HAMMER_INODE_FLUSHW) @@ -786,15 +796,18 @@ hammer_ip_del_directory(struct hammer_transaction *trans, * on-media until we unmount. */ if (error == 0) { - --ip->ino_data.nlinks; + if (ip) + --ip->ino_data.nlinks; /* do before we might block */ dip->ino_data.mtime = trans->time; hammer_modify_inode(dip, HAMMER_INODE_MTIME); - hammer_modify_inode(ip, HAMMER_INODE_DDIRTY); - if (ip->ino_data.nlinks == 0 && - (ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))) { - hammer_done_cursor(cursor); - hammer_inode_unloadable_check(ip, 1); - hammer_flush_inode(ip, 0); + if (ip) { + hammer_modify_inode(ip, HAMMER_INODE_DDIRTY); + if (ip->ino_data.nlinks == 0 && + (ip->vp == NULL || (ip->vp->v_flag & VINACTIVE))) { + hammer_done_cursor(cursor); + hammer_inode_unloadable_check(ip, 1); + hammer_flush_inode(ip, 0); + } } } diff --git a/sys/vfs/hammer/hammer_ondisk.c b/sys/vfs/hammer/hammer_ondisk.c index 214e1ff8dc..cc1bf08874 100644 --- a/sys/vfs/hammer/hammer_ondisk.c +++ b/sys/vfs/hammer/hammer_ondisk.c @@ -48,7 +48,8 @@ static void hammer_free_volume(hammer_volume_t volume); static int hammer_load_volume(hammer_volume_t volume); static int hammer_load_buffer(hammer_buffer_t buffer, int isnew); -static int hammer_load_node(hammer_node_t node, int isnew); +static int hammer_load_node(hammer_transaction_t trans, + hammer_node_t node, int isnew); static int hammer_vol_rb_compare(hammer_volume_t vol1, hammer_volume_t vol2) @@ -1109,7 +1110,7 @@ again: if (node->ondisk) { *errorp = 0; } else { - *errorp = hammer_load_node(node, isnew); + *errorp = hammer_load_node(trans, node, isnew); trans->flags |= HAMMER_TRANSF_DIDIO; } if (*errorp) { @@ -1133,7 +1134,7 @@ hammer_ref_node(hammer_node_t node) * Load a node's on-disk data reference. */ static int -hammer_load_node(hammer_node_t node, int isnew) +hammer_load_node(hammer_transaction_t trans, hammer_node_t node, int isnew) { hammer_buffer_t buffer; hammer_off_t buf_offset; @@ -1175,13 +1176,28 @@ hammer_load_node(hammer_node_t node, int isnew) goto failed; node->ondisk = (void *)((char *)buffer->ondisk + (node->node_offset & HAMMER_BUFMASK)); + + /* + * Check CRC. NOTE: Neither flag is set and the CRC is not + * generated on new B-Tree nodes. + */ if (isnew == 0 && - (node->flags & HAMMER_NODE_CRCGOOD) == 0) { - if (hammer_crc_test_btree(node->ondisk) == 0) - Debugger("CRC FAILED: B-TREE NODE"); - node->flags |= HAMMER_NODE_CRCGOOD; + (node->flags & HAMMER_NODE_CRCANY) == 0) { + if (hammer_crc_test_btree(node->ondisk) == 0) { + if (hammer_debug_debug & 0x0002) + Debugger("CRC FAILED: B-TREE NODE"); + node->flags |= HAMMER_NODE_CRCBAD; + } else { + node->flags |= HAMMER_NODE_CRCGOOD; + } } } + if (node->flags & HAMMER_NODE_CRCBAD) { + if (trans->flags & HAMMER_TRANSF_CRCDOM) + error = EDOM; + else + error = EIO; + } failed: --node->loading; hammer_unlock(&node->lock); @@ -1192,7 +1208,7 @@ failed: * Safely reference a node, interlock against flushes via the IO subsystem. */ hammer_node_t -hammer_ref_node_safe(struct hammer_mount *hmp, hammer_node_cache_t cache, +hammer_ref_node_safe(hammer_transaction_t trans, hammer_node_cache_t cache, int *errorp) { hammer_node_t node; @@ -1200,10 +1216,18 @@ hammer_ref_node_safe(struct hammer_mount *hmp, hammer_node_cache_t cache, node = cache->node; if (node != NULL) { hammer_ref(&node->lock); - if (node->ondisk) - *errorp = 0; - else - *errorp = hammer_load_node(node, 0); + if (node->ondisk) { + if (node->flags & HAMMER_NODE_CRCBAD) { + if (trans->flags & HAMMER_TRANSF_CRCDOM) + *errorp = EDOM; + else + *errorp = EIO; + } else { + *errorp = 0; + } + } else { + *errorp = hammer_load_node(trans, node, 0); + } if (*errorp) { hammer_rel_node(node); node = NULL; diff --git a/sys/vfs/hammer/hammer_vnops.c b/sys/vfs/hammer/hammer_vnops.c index c2abe5f5c1..220a7d5968 100644 --- a/sys/vfs/hammer/hammer_vnops.c +++ b/sys/vfs/hammer/hammer_vnops.c @@ -934,10 +934,25 @@ hammer_vop_nresolve(struct vop_nresolve_args *ap) } } hammer_done_cursor(&cursor); + + /* + * Lookup the obj_id. This should always succeed. If it does not + * the filesystem may be damaged and we return a dummy inode. + */ if (error == 0) { ip = hammer_get_inode(&trans, dip, obj_id, asof, localization, flags, &error); + if (error == ENOENT) { + kprintf("HAMMER: WARNING: Missing " + "inode for dirent \"%s\"\n" + "\tobj_id = %016llx\n", + ncp->nc_name, (long long)obj_id); + error = 0; + ip = hammer_get_dummy_inode(&trans, dip, obj_id, + asof, localization, + flags, &error); + } if (error == 0) { error = hammer_get_vnode(ip, &vp); hammer_rel_inode(ip, 0); @@ -1298,6 +1313,10 @@ hammer_vop_readdir(struct vop_readdir_args *ap) /* * Handle artificial entries + * + * It should be noted that the minimum value for a directory + * hash key on-media is 0x0000000100000000, so we can use anything + * less then that to represent our 'special' key space. */ error = 0; if (saveoff == 0) { @@ -2811,15 +2830,19 @@ retry: 0, &error); hammer_lock_sh(&cursor.ip->lock); if (error == ENOENT) { - kprintf("obj_id %016llx\n", cursor.data->entry.obj_id); - Debugger("ENOENT unlinking object that should exist"); + kprintf("HAMMER: WARNING: Removing " + "dirent w/missing inode \"%s\"\n" + "\tobj_id = %016llx\n", + ncp->nc_name, + (long long)cursor.data->entry.obj_id); + error = 0; } /* * If isdir >= 0 we validate that the entry is or is not a * directory. If isdir < 0 we don't care. */ - if (error == 0 && isdir >= 0) { + if (error == 0 && isdir >= 0 && ip) { if (isdir && ip->ino_data.obj_type != HAMMER_OBJTYPE_DIRECTORY) { error = ENOTDIR; @@ -2841,8 +2864,8 @@ retry: * If any changes whatsoever have been made to the cursor * set EDEADLK and retry. */ - if (error == 0 && ip->ino_data.obj_type == - HAMMER_OBJTYPE_DIRECTORY) { + if (error == 0 && ip && ip->ino_data.obj_type == + HAMMER_OBJTYPE_DIRECTORY) { hammer_unlock_cursor(&cursor); error = hammer_ip_check_directory_empty(trans, ip); hammer_lock_cursor(&cursor); @@ -2870,7 +2893,7 @@ retry: cache_setunresolved(nch); cache_setvp(nch, NULL); /* XXX locking */ - if (ip->vp) { + if (ip && ip->vp) { hammer_knote(ip->vp, NOTE_DELETE); cache_inval_vp(ip->vp, CINV_DESTROY); } -- 2.41.0