Author: jeff
Date: Tue Jul  6 07:07:29 2010
New Revision: 209716
URL: http://svn.freebsd.org/changeset/base/209716

Log:
   - Permit zero length directories as a handled inconsistency.  This allows
     directory truncation to proceed before the link has been cleared.  This
     is accomplished by detecting a directory with no . or .. links and
     clearing the named directory entry in the parent.
   - Add a new function ino_remref() which handles the details of removing
     a reference to an inode as a result of a lost directory.  There were
     some minor errors in various subcases of this routine.

Modified:
  head/sbin/fsck_ffs/suj.c

Modified: head/sbin/fsck_ffs/suj.c
==============================================================================
--- head/sbin/fsck_ffs/suj.c    Tue Jul  6 03:48:46 2010        (r209715)
+++ head/sbin/fsck_ffs/suj.c    Tue Jul  6 07:07:29 2010        (r209716)
@@ -808,6 +808,44 @@ blk_isat(ino_t ino, ufs_lbn_t lbn, ufs2_
 }
 
 /*
+ * Clear the directory entry at diroff that should point to child.  Minimal
+ * checking is done and it is assumed that this path was verified with isat.
+ */
+static void
+ino_clrat(ino_t parent, off_t diroff, ino_t child)
+{
+       union dinode *dip;
+       struct direct *dp;
+       ufs2_daddr_t blk;
+       uint8_t *block;
+       ufs_lbn_t lbn;
+       int blksize;
+       int frags;
+       int doff;
+
+       if (debug)
+               printf("Clearing inode %d from parent %d at offset %jd\n",
+                   child, parent, diroff);
+
+       lbn = lblkno(fs, diroff);
+       doff = blkoff(fs, diroff);
+       dip = ino_read(parent);
+       blk = ino_blkatoff(dip, parent, lbn, &frags);
+       blksize = sblksize(fs, DIP(dip, di_size), lbn);
+       block = dblk_read(blk, blksize);
+       dp = (struct direct *)&block[doff];
+       if (dp->d_ino != child)
+               errx(1, "Inode %d does not exist in %d at %jd",
+                   child, parent, diroff);
+       dp->d_ino = 0;
+       dblk_dirty(blk);
+       /*
+        * The actual .. reference count will already have been removed
+        * from the parent by the .. remref record.
+        */
+}
+
+/*
  * Determines whether a pointer to an inode exists within a directory
  * at a specified offset.  Returns the mode of the found entry.
  */
@@ -1134,6 +1172,57 @@ ino_setskip(struct suj_ino *sino, ino_t 
                sino->si_skipparent = 1;
 }
 
+static void
+ino_remref(ino_t parent, ino_t child, uint64_t diroff, int isdotdot)
+{
+       struct suj_ino *sino;
+       struct suj_rec *srec;
+       struct jrefrec *rrec;
+
+       /*
+        * Lookup this inode to see if we have a record for it.
+        */
+       sino = ino_lookup(child, 0);
+       /*
+        * Tell any child directories we've already removed their
+        * parent link cnt.  Don't try to adjust our link down again.
+        */
+       if (sino != NULL && isdotdot == 0)
+               ino_setskip(sino, parent);
+       /*
+        * No valid record for this inode.  Just drop the on-disk
+        * link by one.
+        */
+       if (sino == NULL || sino->si_hasrecs == 0) {
+               ino_decr(child);
+               return;
+       }
+       /*
+        * Use ino_adjust() if ino_check() has already processed this
+        * child.  If we lose the last non-dot reference to a
+        * directory it will be discarded.
+        */
+       if (sino->si_linkadj) {
+               sino->si_nlink--;
+               if (isdotdot)
+                       sino->si_dotlinks--;
+               ino_adjust(sino);
+               return;
+       }
+       /*
+        * If we haven't yet processed this inode we need to make
+        * sure we will successfully discover the lost path.  If not
+        * use nlinkadj to remember.
+        */
+       TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
+               rrec = (struct jrefrec *)srec->sr_rec;
+               if (rrec->jr_parent == parent &&
+                   rrec->jr_diroff == diroff)
+                       return;
+       }
+       sino->si_nlinkadj++;
+}
+
 /*
  * Free the children of a directory when the directory is discarded.
  */
@@ -1141,13 +1230,11 @@ static void
 ino_free_children(ino_t ino, ufs_lbn_t lbn, ufs2_daddr_t blk, int frags)
 {
        struct suj_ino *sino;
-       struct suj_rec *srec;
-       struct jrefrec *rrec;
        struct direct *dp;
        off_t diroff;
        uint8_t *block;
        int skipparent;
-       int isparent;
+       int isdotdot;
        int dpoff;
        int size;
 
@@ -1165,53 +1252,15 @@ ino_free_children(ino_t ino, ufs_lbn_t l
                        continue;
                if (dp->d_namlen == 1 && dp->d_name[0] == '.')
                        continue;
-               isparent = dp->d_namlen == 2 && dp->d_name[0] == '.' &&
+               isdotdot = dp->d_namlen == 2 && dp->d_name[0] == '.' &&
                    dp->d_name[1] == '.';
-               if (isparent && skipparent == 1)
+               if (isdotdot && skipparent == 1)
                        continue;
                if (debug)
                        printf("Directory %d removing ino %d name %s\n",
                            ino, dp->d_ino, dp->d_name);
-               /*
-                * Lookup this inode to see if we have a record for it.
-                * If not, we've already adjusted it assuming this path
-                * was valid and we have to adjust once more.
-                */
-               sino = ino_lookup(dp->d_ino, 0);
-               if (sino == NULL || sino->si_hasrecs == 0) {
-                       ino_decr(ino);
-                       continue;
-               }
-               /*
-                * Use ino_adjust() so if we lose the last non-dot reference
-                * to a directory it can be discarded.
-                */
-               if (sino->si_linkadj) {
-                       sino->si_nlink--;
-                       if (isparent)
-                               sino->si_dotlinks--;
-                       ino_adjust(sino);
-               }
-               /*
-                * Tell any child directories we've already removed their
-                * parent.  Don't try to adjust our link down again.
-                */
-               if (isparent == 0)
-                       ino_setskip(sino, ino);
-               /*
-                * If we haven't yet processed this inode we need to make
-                * sure we will successfully discover the lost path.  If not
-                * use nlinkadj to remember.
-                */
                diroff = lblktosize(fs, lbn) + dpoff;
-               TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
-                       rrec = (struct jrefrec *)srec->sr_rec;
-                       if (rrec->jr_parent == ino &&
-                           rrec->jr_diroff == diroff)
-                               break;
-               }
-               if (srec == NULL)
-                       sino->si_nlinkadj++;
+               ino_remref(ino, dp->d_ino, diroff, isdotdot);
        }
 }
 
@@ -1293,18 +1342,38 @@ ino_adjust(struct suj_ino *sino)
        struct suj_ino *stmp;
        union dinode *ip;
        nlink_t nlink;
+       int recmode;
        int reqlink;
+       int isdot;
        int mode;
        ino_t ino;
 
        nlink = sino->si_nlink;
        ino = sino->si_ino;
+       mode = sino->si_mode & IFMT;
+       /*
+        * If it's a directory with no dot links, it was truncated before
+        * the name was cleared.  We need to clear the dirent that
+        * points at it.
+        */
+       if (mode == IFDIR && nlink == 1 && sino->si_dotlinks == 0) {
+               sino->si_nlink = nlink = 0;
+               TAILQ_FOREACH(srec, &sino->si_recs, sr_next) {
+                       rrec = (struct jrefrec *)srec->sr_rec;
+                       if (ino_isat(rrec->jr_parent, rrec->jr_diroff, ino,
+                           &recmode, &isdot) == 0)
+                               continue;
+                       ino_clrat(rrec->jr_parent, rrec->jr_diroff, ino);
+                       break;
+               }
+               if (srec == NULL)
+                       errx(1, "Directory %d name not found", ino);
+       }
        /*
         * If it's a directory with no real names pointing to it go ahead
         * and truncate it.  This will free any children.
         */
-       if ((sino->si_mode & IFMT) == IFDIR &&
-           nlink - sino->si_dotlinks == 0) {
+       if (mode == IFDIR && nlink - sino->si_dotlinks == 0) {
                sino->si_nlink = nlink = 0;
                /*
                 * Mark any .. links so they know not to free this inode
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to