Whamcloud - gitweb
LU-17711 osd-ldiskfs: do not delete dotdot during rename
[fs/lustre-release.git] / ldiskfs / kernel_patches / patches / sles15sp3 / ext4-data-in-dirent.patch
index c016423..6fbbcdc 100644 (file)
@@ -182,7 +182,7 @@ index 8577b51..dab4486 100644
                             void *buf, int buf_size,
                             struct ext4_filename *fname,
 -                           struct ext4_dir_entry_2 **dest_de);
-+                           struct ext4_dir_entry_2 **dest_de, int *dlen);
++                           struct ext4_dir_entry_2 **dest_de, int dlen);
  void ext4_insert_dentry(struct inode *inode,
                        struct ext4_dir_entry_2 *de,
                        int buf_size,
@@ -266,7 +266,7 @@ index 46151bd..316892b 100644
  
        err = ext4_find_dest_de(dir, inode, iloc->bh, inline_start,
 -                              inline_size, fname, &de);
-+                              inline_size, fname, &de, NULL);
++                              inline_size, fname, &de, 0);
        if (err)
                return err;
  
@@ -423,26 +423,20 @@ index 54cca61..2bc4682 100644
                        if (de > to)
                                memmove(to, de, rec_len);
                        to->rec_len = ext4_rec_len_to_disk(rec_len, blocksize);
-@@ -1954,14 +1966,16 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+@@ -1954,10 +1966,10 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
                      struct buffer_head *bh,
                      void *buf, int buf_size,
                      struct ext4_filename *fname,
 -                    struct ext4_dir_entry_2 **dest_de)
-+                    struct ext4_dir_entry_2 **dest_de, int *dlen)
++                    struct ext4_dir_entry_2 **dest_de, int dlen)
  {
        struct ext4_dir_entry_2 *de;
 -      unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname));
-+      unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname)) +
-+                                                (dlen ? *dlen : 0);
++      unsigned short reclen = EXT4_DIR_REC_LEN(fname_len(fname) + dlen);
        int nlen, rlen;
        unsigned int offset = 0;
        char *top;
-+      dlen ? *dlen = 0 : 0; /* default set to 0 */
-       de = (struct ext4_dir_entry_2 *)buf;
-       top = buf + buf_size - reclen;
-       while ((char *) de <= top) {
-@@ -1970,10 +1984,26 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
+@@ -1970,7 +1984,7 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
                        return -EFSCORRUPTED;
                if (ext4_match(dir, fname, de))
                        return -EEXIST;
@@ -451,25 +445,6 @@ index 54cca61..2bc4682 100644
                rlen = ext4_rec_len_from_disk(de->rec_len, buf_size);
                if ((de->inode ? rlen - nlen : rlen) >= reclen)
                        break;
-+              /* Then for dotdot entries, check for the smaller space
-+               * required for just the entry, no FID */
-+              if (fname_len(fname) == 2 && memcmp(fname_name(fname), "..", 2) == 0) {
-+                      if ((de->inode ? rlen - nlen : rlen) >=
-+                          EXT4_DIR_REC_LEN(fname_len(fname))) {
-+                              /* set dlen=1 to indicate not
-+                               * enough space store fid */
-+                              dlen ? *dlen = 1 : 0;
-+                              break;
-+                      }
-+                      /* The new ".." entry must be written over the
-+                       * previous ".." entry, which is the first
-+                       * entry traversed by this scan. If it doesn't
-+                       * fit, something is badly wrong, so -EIO. */
-+                      return -EIO;
-+              }
-               de = (struct ext4_dir_entry_2 *)((char *)de + rlen);
-               offset += rlen;
-       }
 @@ -1987,12 +2017,12 @@ int ext4_find_dest_de(struct inode *dir, struct inode *inode,
  void ext4_insert_dentry(struct inode *inode,
                        struct ext4_dir_entry_2 *de,
@@ -516,18 +491,15 @@ index 54cca61..2bc4682 100644
 +                      dlen = (*data) + 1;
                err = ext4_find_dest_de(dir, inode, bh, bh->b_data,
 -                                      blocksize - csum_size, fname, &de);
-+                                      blocksize - csum_size, fname, &de, &dlen);
++                                      blocksize - csum_size, fname, &de, dlen);
                if (err)
                        return err;
        }
-@@ -2042,7 +2083,10 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
+@@ -2042,7 +2083,7 @@ static int add_dirent_to_buf(handle_t *handle, struct ext4_filename *fname,
        }
  
        /* By now the buffer is marked for journaling */
 -      ext4_insert_dentry(inode, de, blocksize, fname);
-+      /* If writing the short form of "dotdot", don't add the data section */
-+      if (dlen == 1)
-+              data = NULL;
 +      ext4_insert_dentry(inode, de, blocksize, fname, data);
  
        /*
@@ -542,49 +514,152 @@ index 54cca61..2bc4682 100644
  
        /* Initialize as for dx_probe */
        fname->hinfo.hash_version = dx_info->hash_version;
-@@ -2197,6 +2242,8 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
-       struct buffer_head *dir_block;
-       struct ext4_dir_entry_2 *de;
-       int len, journal = 0, err = 0;
+@@ -2189,7 +2213,104 @@ out_frames:
+       return retval;
+ }
+-/* update ".." entry */
++static int ext4_expand_dotdot(struct inode *dir,
++                            struct buffer_head *bh,
++                            int dlen)
++{
++      struct ext4_dir_entry_2 *dot_de;
++      struct ext4_dir_entry_2 *dotdot_de;
++      int len;
++      unsigned blocksize = dir->i_sb->s_blocksize;
++
++      dot_de = (struct ext4_dir_entry_2 *)bh->b_data;
++      dotdot_de = ext4_next_entry(dot_de, blocksize);
++
++      if (is_dx(dir)) {
++              struct dx_entry *entries;
++              struct dx_root_info *dx_info;
++              int limit, count;
++              int entry_space;
++
++              len = EXT4_DIR_REC_LEN(2 + dlen) -
++                      EXT4_DIR_ENTRY_LEN(dotdot_de);
++
++              dx_info = dx_get_dx_info(dot_de);
++              entries = (struct dx_entry *)((char *)dx_info +
++                                                      sizeof(*dx_info));
++              count = dx_get_count(entries);
++
++              /*
++               * figure out new limit with dlen,
++               * check if we have enough space
++               */
++              entry_space = blocksize;
++              entry_space -= (char *)dotdot_de - (char *)dot_de +
++                             EXT4_DIR_REC_LEN(2 + dlen) + sizeof(*dx_info);
++              if (ext4_has_metadata_csum(dir->i_sb))
++                      entry_space -= sizeof(struct dx_tail);
++              limit = entry_space / sizeof(struct dx_entry);
++              if (count > limit)
++                      return -ENOSPC;
++
++              /* set the new limit, move dx_info and the entries */
++              dx_set_limit(entries, limit);
++              memmove((char *)dx_info + len, dx_info,
++                      sizeof(*dx_info) + count * sizeof(struct dx_entry));
++      } else {
++              struct ext4_dir_entry_2 *next, *to, *prev, *de;
++              char *top = (char *)bh->b_data + blocksize;
++              int space = 0;
++              unsigned rec_len = 0;
++
++              len = EXT4_DIR_REC_LEN(2 + dlen) -
++                      ext4_rec_len_from_disk(dotdot_de->rec_len, blocksize);
++
++              if (ext4_has_metadata_csum(dir->i_sb))
++                      top -= sizeof(struct ext4_dir_entry_tail);
++
++              de = ext4_next_entry(dotdot_de, blocksize);
++              while ((char *)de < top) {
++                      space += ext4_rec_len_from_disk(de->rec_len, blocksize) -
++                                      EXT4_DIR_ENTRY_LEN(de);
++                      de = ext4_next_entry(de, blocksize);
++              }
++
++              if (space < len)
++                      return -ENOSPC;
++
++              /* pack all the entries after dotdot */
++              de = ext4_next_entry(dotdot_de, blocksize);
++              prev = to = de;
++              while ((char *)de < top) {
++                      next = ext4_next_entry(de, blocksize);
++                      if (de->inode && de->name_len) {
++                              rec_len = EXT4_DIR_ENTRY_LEN(de);
++                              if (de > to)
++                                      memmove(to, de, rec_len);
++                              to->rec_len = ext4_rec_len_to_disk(rec_len,
++                                                                 blocksize);
++                              prev = to;
++                              to = (struct ext4_dir_entry_2 *)
++                                              (((char *)to) + rec_len);
++                      }
++                      de = next;
++              }
++              /* fix up rec_len for the last entry */
++              prev->rec_len = ext4_rec_len_to_disk(top - (char *)prev - len,
++                                                   blocksize);
++              /* move all the entries after dotdot to make space */
++              de = ext4_next_entry(dotdot_de, blocksize);
++              memmove((char *)de + len, de, (char *)prev - (char *)de +
++                      EXT4_DIR_ENTRY_LEN(prev));
++              /* fix the rec_len for dotdot */
++              dotdot_de->rec_len = ext4_rec_len_to_disk(
++                                      EXT4_DIR_REC_LEN(2 + dlen), blocksize);
++      }
++
++      return 0;
++}
++
++/* update ".." entry, try to expand the entry if necessary */
+ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
+                             struct inode *inode)
+ {
+@@ -2198,6 +2319,8 @@ static int ext4_update_dotdot(handle_t *
+       struct ext4_dir_entry_2 *dot_de, *dotdot_de;
+       unsigned int offset;
+       int retval = 0;
 +      int dlen = 0;
 +      char *data;
  
        if (IS_ERR(handle))
                return PTR_ERR(handle);
-@@ -2222,11 +2269,16 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
-                       goto out_journal;
+@@ -2237,6 +2360,30 @@ static int ext4_update_dotdot(handle_t *
  
-               journal = 1;
--              de->rec_len = cpu_to_le16(EXT4_DIR_REC_LEN(1));
-+              de->rec_len = cpu_to_le16(EXT4_DIR_ENTRY_LEN(de));
-       }
+       dotdot_de->inode = cpu_to_le32(inode->i_ino);
  
--      len -= EXT4_DIR_REC_LEN(1);
--      assert(len == 0 || len >= EXT4_DIR_REC_LEN(2));
-+      len -= EXT4_DIR_ENTRY_LEN(de);
 +      data = ext4_dentry_get_data(dir->i_sb,
 +                      (struct ext4_dentry_param *)dentry->d_fsdata);
-+      if (data)
++      if (data != NULL) {
 +              dlen = *data + 1;
-+      assert(len == 0 || len >= EXT4_DIR_REC_LEN(2 + dlen));
-+
-       de = (struct ext4_dir_entry_2 *)
-                       ((char *) de + le16_to_cpu(de->rec_len));
-       if (!journal) {
-@@ -2243,7 +2295,12 @@ static int ext4_update_dotdot(handle_t *handle, struct dentry *dentry,
-               assert(le16_to_cpu(de->rec_len) >= EXT4_DIR_REC_LEN(2));
-       de->name_len = 2;
-       strcpy(de->name, "..");
--      ext4_set_de_type(dir->i_sb, de, S_IFDIR);
-+      if (data != NULL && ext4_get_dirent_data_len(de) >= dlen) {
-+              de->name[2] = 0;
-+              memcpy(&de->name[2 + 1], data, *data);
-+              ext4_set_de_type(dir->i_sb, de, S_IFDIR);
-+              de->file_type |= EXT4_DIRENT_LUFID;
++              if (is_dx(dir)) {
++                      if (ext4_get_dirent_data_len(dotdot_de) < dlen) {
++                              if (ext4_expand_dotdot(dir, bh, dlen) < 0)
++                                      dlen = 0;
++                      }
++              } else {
++                      if (ext4_rec_len_from_disk(dotdot_de->rec_len,
++                                                 dir->i_sb->s_blocksize) <
++                          EXT4_DIR_REC_LEN(2 + dlen)) {
++                              if (ext4_expand_dotdot(dir, bh, dlen) < 0)
++                                      dlen = 0;
++                      }
++              }
 +      }
- out_journal:
-       if (journal) {
++      if (dlen) {
++              dotdot_de->name[2] = 0;
++              memcpy(&dotdot_de->name[2 + 1], data, *data);
++              dotdot_de->file_type |= LDISKFS_DIRENT_LUFID;
++      }
++
+       ext4_mark_inode_dirty(handle, dir);
+       BUFFER_TRACE(dir_block, "call ext4_handle_dirty_metadata");
+       if (is_dx(dir)) {
 @@ -2282,6 +2339,7 @@ static int ext4_add_entry(handle_t *handle, struct dentry *dentry,
        ext4_lblk_t block, blocks;
        int     csum_size = 0;