summaryrefslogtreecommitdiffstats
path: root/sbin/fsck_ffs
diff options
context:
space:
mode:
authormckusick <mckusick@FreeBSD.org>2010-01-11 19:52:40 +0000
committermckusick <mckusick@FreeBSD.org>2010-01-11 19:52:40 +0000
commit25670cd155c4c56bcb69828cc33d068e0223b8a6 (patch)
treeadd76af0f5b98d462ac458946b811d2a9089080e /sbin/fsck_ffs
parent2fea4e0cb13865b2ee6cb309ad66244cfc067a1f (diff)
downloadFreeBSD-src-25670cd155c4c56bcb69828cc33d068e0223b8a6.zip
FreeBSD-src-25670cd155c4c56bcb69828cc33d068e0223b8a6.tar.gz
When renaming a directory it passes through several intermediate
states. First its new name will be created causing it to have two names (from possibly different parents). Next, if it has different parents, its value of ".." will be changed from pointing to the old parent to pointing to the new parent. Concurrently, its old name will be removed bringing it back into a consistent state. When fsck encounters an extra name for a directory, it offers to remove the "extraneous hard link"; when it finds that the names have been changed but the update to ".." has not happened, it offers to rewrite ".." to point at the correct parent. Both of these changes were considered unexpected so would cause fsck in preen mode or fsck in background mode to fail with the need to run fsck manually to fix these problems. This update changes these errors to be expected so that in preen mode fsck will simply fix these transitional errors. For now, background fsck will note these errors, but will need additional kernel support to fix them, so will simply ignore them rather than fail. A future update will allow background fsck to fix these problems. Reported by: jeff
Diffstat (limited to 'sbin/fsck_ffs')
-rw-r--r--sbin/fsck_ffs/pass2.c130
1 files changed, 109 insertions, 21 deletions
diff --git a/sbin/fsck_ffs/pass2.c b/sbin/fsck_ffs/pass2.c
index 59a7a84..ffe5fb7 100644
--- a/sbin/fsck_ffs/pass2.c
+++ b/sbin/fsck_ffs/pass2.c
@@ -49,6 +49,8 @@ __FBSDID("$FreeBSD$");
#define MINDIRSIZE (sizeof (struct dirtemplate))
+static int fix_extraneous(struct inoinfo *, struct inodesc *);
+static int deleteentry(struct inodesc *);
static int blksort(const void *, const void *);
static int pass2check(struct inodesc *);
@@ -236,8 +238,6 @@ pass2check(struct inodesc *idesc)
union dinode *dp;
const char *errmsg;
struct direct proto;
- char namebuf[MAXPATHLEN + 1];
- char pathbuf[MAXPATHLEN + 1];
/*
* check for "."
@@ -416,27 +416,12 @@ again:
case DFOUND:
inp = getinoinfo(dirp->d_ino);
- if (inp->i_parent != 0 && idesc->id_entryno > 2) {
- getpathname(pathbuf, idesc->id_number,
- idesc->id_number);
- getpathname(namebuf, dirp->d_ino, dirp->d_ino);
- pwarn("%s%s%s %s %s\n", pathbuf,
- (strcmp(pathbuf, "/") == 0 ? "" : "/"),
- dirp->d_name,
- "IS AN EXTRANEOUS HARD LINK TO DIRECTORY",
- namebuf);
- if (cursnapshot != 0)
- break;
- if (preen) {
- printf(" (REMOVED)\n");
- n = 1;
- break;
- }
- if ((n = reply("REMOVE")) == 1)
+ if (idesc->id_entryno > 2) {
+ if (inp->i_parent == 0)
+ inp->i_parent = idesc->id_number;
+ else if ((n = fix_extraneous(inp, idesc)) == 1)
break;
}
- if (idesc->id_entryno > 2)
- inp->i_parent = idesc->id_number;
/* FALLTHROUGH */
case FSTATE:
@@ -462,6 +447,109 @@ again:
return (ret|KEEPON|ALTERED);
}
+static int
+fix_extraneous(struct inoinfo *inp, struct inodesc *idesc)
+{
+ struct inodesc dotdesc;
+ char oldname[MAXPATHLEN + 1];
+ char newname[MAXPATHLEN + 1];
+
+ /*
+ * If we have not yet found "..", look it up now so we know
+ * which inode the directory itself believes is its parent.
+ */
+ if (inp->i_dotdot == 0) {
+ memset(&dotdesc, 0, sizeof(struct inodesc));
+ dotdesc.id_type = DATA;
+ dotdesc.id_number = idesc->id_dirp->d_ino;
+ dotdesc.id_func = findino;
+ dotdesc.id_name = strdup("..");
+ if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND))
+ inp->i_dotdot = dotdesc.id_parent;
+ }
+ /*
+ * We have the previously found old name (inp->i_parent) and the
+ * just found new name (idesc->id_number). We have five cases:
+ * 1) ".." is missing - can remove either name, choose to delete
+ * new one and let fsck create ".." pointing to old name.
+ * 2) Both new and old are in same directory, choose to delete
+ * the new name and let fsck fix ".." if it is wrong.
+ * 3) ".." does not point to the new name, so delete it and let
+ * fsck fix ".." to point to the old one if it is wrong.
+ * 4) ".." points to the old name only, so delete the new one.
+ * 5) ".." points to the new name only, so delete the old one.
+ *
+ * For cases 1-4 we eliminate the new name;
+ * for case 5 we eliminate the old name.
+ */
+ if (inp->i_dotdot == 0 || /* Case 1 */
+ idesc->id_number == inp->i_parent || /* Case 2 */
+ inp->i_dotdot != idesc->id_number || /* Case 3 */
+ inp->i_dotdot == inp->i_parent) { /* Case 4 */
+ getpathname(newname, idesc->id_number, idesc->id_number);
+ if (strcmp(newname, "/") != 0)
+ strcat (newname, "/");
+ strcat(newname, idesc->id_dirp->d_name);
+ getpathname(oldname, inp->i_number, inp->i_number);
+ pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n",
+ newname, oldname);
+ if (cursnapshot != 0) {
+ /*
+ * We need to
+ * setcwd(idesc->id_number);
+ * unlink(idesc->id_dirp->d_name);
+ */
+ printf(" (IGNORED)\n");
+ return (0);
+ }
+ if (preen) {
+ printf(" (REMOVED)\n");
+ return (1);
+ }
+ return (reply("REMOVE"));
+ }
+ /*
+ * None of the first four cases above, so must be case (5).
+ * Eliminate the old name and make the new the name the parent.
+ */
+ getpathname(oldname, inp->i_parent, inp->i_number);
+ getpathname(newname, inp->i_number, inp->i_number);
+ pwarn("%s IS AN EXTRANEOUS HARD LINK TO DIRECTORY %s\n", oldname,
+ newname);
+ if (cursnapshot != 0) {
+ /*
+ * We need to
+ * setcwd(inp->i_parent);
+ * unlink(last component of oldname pathname);
+ */
+ printf(" (IGNORED)\n");
+ return (0);
+ }
+ if (!preen && !reply("REMOVE"))
+ return (0);
+ memset(&dotdesc, 0, sizeof(struct inodesc));
+ dotdesc.id_type = DATA;
+ dotdesc.id_number = inp->i_parent; /* directory in which name appears */
+ dotdesc.id_parent = inp->i_number; /* inode number in entry to delete */
+ dotdesc.id_func = deleteentry;
+ if ((ckinode(ginode(dotdesc.id_number), &dotdesc) & FOUND) && preen)
+ printf(" (REMOVED)\n");
+ inp->i_parent = idesc->id_number; /* reparent to correct directory */
+ inoinfo(inp->i_number)->ino_linkcnt++; /* name gone, return reference */
+ return (0);
+}
+
+static int
+deleteentry(struct inodesc *idesc)
+{
+ struct direct *dirp = idesc->id_dirp;
+
+ if (idesc->id_entryno++ < 2 || dirp->d_ino != idesc->id_parent)
+ return (KEEPON);
+ dirp->d_ino = 0;
+ return (ALTERED|STOP|FOUND);
+}
+
/*
* Routine to sort disk blocks.
*/
OpenPOWER on IntegriCloud