Whamcloud - gitweb
libext2fs/ismounted.c: check device id in advance to skip false device names
[tools/e2fsprogs.git] / lib / ext2fs / link.c
index f715067..65dc887 100644 (file)
@@ -42,6 +42,10 @@ static int link_proc(struct ext2_dir_entry *dirent,
        unsigned int rec_len, min_rec_len, curr_rec_len;
        int ret = 0;
        int csum_size = 0;
+       struct ext2_dir_entry_tail *t;
+
+       if (ls->done)
+               return DIRENT_ABORT;
 
        rec_len = EXT2_DIR_REC_LEN(ls->namelen);
 
@@ -49,8 +53,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
        if (ls->err)
                return DIRENT_ABORT;
 
-       if (EXT2_HAS_RO_COMPAT_FEATURE(ls->fs->super,
-                                      EXT4_FEATURE_RO_COMPAT_METADATA_CSUM))
+       if (ext2fs_has_feature_metadata_csum(ls->fs->super))
                csum_size = sizeof(struct ext2_dir_entry_tail);
        /*
         * See if the following directory entry (if any) is unused;
@@ -68,6 +71,40 @@ static int link_proc(struct ext2_dir_entry *dirent,
        }
 
        /*
+        * Since ext2fs_link blows away htree data, we need to be
+        * careful -- if metadata_csum is enabled and we're passed in
+        * a dirent that contains htree data, we need to create the
+        * fake entry at the end of the block that hides the checksum.
+        */
+
+       /* De-convert a dx_node block */
+       if (csum_size &&
+           curr_rec_len == ls->fs->blocksize &&
+           !dirent->inode) {
+               curr_rec_len -= csum_size;
+               ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
+               if (ls->err)
+                       return DIRENT_ABORT;
+               t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
+               ext2fs_initialize_dirent_tail(ls->fs, t);
+               ret = DIRENT_CHANGED;
+       }
+
+       /* De-convert a dx_root block */
+       if (csum_size &&
+           curr_rec_len == ls->fs->blocksize - EXT2_DIR_REC_LEN(1) &&
+           offset == EXT2_DIR_REC_LEN(1) &&
+           dirent->name[0] == '.' && dirent->name[1] == '.') {
+               curr_rec_len -= csum_size;
+               ls->err = ext2fs_set_rec_len(ls->fs, curr_rec_len, dirent);
+               if (ls->err)
+                       return DIRENT_ABORT;
+               t = EXT2_DIRENT_TAIL(buf, ls->fs->blocksize);
+               ext2fs_initialize_dirent_tail(ls->fs, t);
+               ret = DIRENT_CHANGED;
+       }
+
+       /*
         * If the directory entry is used, see if we can split the
         * directory entry to make room for the new name.  If so,
         * truncate it and return.
@@ -100,7 +137,7 @@ static int link_proc(struct ext2_dir_entry *dirent,
        dirent->inode = ls->inode;
        ext2fs_dirent_set_name_len(dirent, ls->namelen);
        strncpy(dirent->name, ls->name, ls->namelen);
-       if (ls->sb->s_feature_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE)
+       if (ext2fs_has_feature_filetype(ls->sb))
                ext2fs_dirent_set_file_type(dirent, ls->flags & 0x7);
 
        ls->done++;
@@ -149,6 +186,11 @@ errcode_t ext2fs_link(ext2_filsys fs, ext2_ino_t dir, const char *name,
        if ((retval = ext2fs_read_inode(fs, dir, &inode)) != 0)
                return retval;
 
+       /*
+        * If this function changes to preserve the htree, remove the
+        * two hunks in link_proc that shove checksum tails into the
+        * former dx_root/dx_node blocks.
+        */
        if (inode.i_flags & EXT2_INDEX_FL) {
                inode.i_flags &= ~EXT2_INDEX_FL;
                if ((retval = ext2fs_write_inode(fs, dir, &inode)) != 0)