udf: Implement adding of dir entries using new iteration code
authorJan Kara <jack@suse.cz>
Thu, 6 Oct 2022 11:25:16 +0000 (13:25 +0200)
committerJan Kara <jack@suse.cz>
Mon, 9 Jan 2023 09:39:52 +0000 (10:39 +0100)
Implement function udf_fiiter_add_entry() adding new directory entries
using new directory iteration code.

Signed-off-by: Jan Kara <jack@suse.cz>
fs/udf/directory.c
fs/udf/namei.c
fs/udf/udfdecl.h

index 2842c46..be85d1b 100644 (file)
@@ -413,6 +413,63 @@ void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse)
        inode_inc_iversion(iter->dir);
 }
 
+void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen)
+{
+       struct udf_inode_info *iinfo = UDF_I(iter->dir);
+       int diff = new_elen - iter->elen;
+
+       /* Skip update when we already went past the last extent */
+       if (!iter->elen)
+               return;
+       iter->elen = new_elen;
+       if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
+               iter->epos.offset -= sizeof(struct short_ad);
+       else if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_LONG)
+               iter->epos.offset -= sizeof(struct long_ad);
+       udf_write_aext(iter->dir, &iter->epos, &iter->eloc, iter->elen, 1);
+       iinfo->i_lenExtents += diff;
+       mark_inode_dirty(iter->dir);
+}
+
+/* Append new block to directory. @iter is expected to point at EOF */
+int udf_fiiter_append_blk(struct udf_fileident_iter *iter)
+{
+       struct udf_inode_info *iinfo = UDF_I(iter->dir);
+       int blksize = 1 << iter->dir->i_blkbits;
+       struct buffer_head *bh;
+       sector_t block;
+       uint32_t old_elen = iter->elen;
+       int err;
+
+       if (WARN_ON_ONCE(iinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB))
+               return -EINVAL;
+
+       /* Round up last extent in the file */
+       udf_fiiter_update_elen(iter, ALIGN(iter->elen, blksize));
+
+       /* Allocate new block and refresh mapping information */
+       block = iinfo->i_lenExtents >> iter->dir->i_blkbits;
+       bh = udf_bread(iter->dir, block, 1, &err);
+       if (!bh) {
+               udf_fiiter_update_elen(iter, old_elen);
+               return err;
+       }
+       if (inode_bmap(iter->dir, block, &iter->epos, &iter->eloc, &iter->elen,
+                      &iter->loffset) != (EXT_RECORDED_ALLOCATED >> 30)) {
+               udf_err(iter->dir->i_sb,
+                       "block %llu not allocated in directory (ino %lu)\n",
+                       (unsigned long long)block, iter->dir->i_ino);
+               return -EFSCORRUPTED;
+       }
+       if (!(iter->pos & (blksize - 1))) {
+               brelse(iter->bh[0]);
+               iter->bh[0] = bh;
+       } else {
+               iter->bh[1] = bh;
+       }
+       return 0;
+}
+
 struct fileIdentDesc *udf_fileident_read(struct inode *dir, loff_t *nf_pos,
                                         struct udf_fileident_bh *fibh,
                                         struct fileIdentDesc *cfi,
index d0ffd20..2e423c1 100644 (file)
@@ -472,6 +472,116 @@ static struct buffer_head *udf_expand_dir_adinicb(struct inode *inode,
        return dbh;
 }
 
+static int udf_fiiter_add_entry(struct inode *dir, struct dentry *dentry,
+                               struct udf_fileident_iter *iter)
+{
+       struct udf_inode_info *dinfo = UDF_I(dir);
+       int nfidlen, namelen = 0;
+       int ret;
+       int off, blksize = 1 << dir->i_blkbits;
+       udf_pblk_t block;
+       char name[UDF_NAME_LEN_CS0];
+
+       if (dentry) {
+               if (!dentry->d_name.len)
+                       return -EINVAL;
+               namelen = udf_put_filename(dir->i_sb, dentry->d_name.name,
+                                          dentry->d_name.len,
+                                          name, UDF_NAME_LEN_CS0);
+               if (!namelen)
+                       return -ENAMETOOLONG;
+       }
+       nfidlen = ALIGN(sizeof(struct fileIdentDesc) + namelen, UDF_NAME_PAD);
+
+       for (ret = udf_fiiter_init(iter, dir, 0);
+            !ret && iter->pos < dir->i_size;
+            ret = udf_fiiter_advance(iter)) {
+               if (iter->fi.fileCharacteristics & FID_FILE_CHAR_DELETED) {
+                       if (udf_dir_entry_len(&iter->fi) == nfidlen) {
+                               iter->fi.descTag.tagSerialNum = cpu_to_le16(1);
+                               iter->fi.fileVersionNum = cpu_to_le16(1);
+                               iter->fi.fileCharacteristics = 0;
+                               iter->fi.lengthFileIdent = namelen;
+                               iter->fi.lengthOfImpUse = cpu_to_le16(0);
+                               memcpy(iter->namebuf, name, namelen);
+                               iter->name = iter->namebuf;
+                               return 0;
+                       }
+               }
+       }
+       if (ret) {
+               udf_fiiter_release(iter);
+               return ret;
+       }
+       if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB &&
+           blksize - udf_ext0_offset(dir) - iter->pos < nfidlen) {
+               struct buffer_head *retbh;
+
+               udf_fiiter_release(iter);
+               /*
+                * FIXME: udf_expand_dir_adinicb does not need to return bh
+                * once other users are gone
+                */
+               retbh = udf_expand_dir_adinicb(dir, &block, &ret);
+               if (!retbh)
+                       return ret;
+               brelse(retbh);
+               ret = udf_fiiter_init(iter, dir, dir->i_size);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Get blocknumber to use for entry tag */
+       if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+               block = dinfo->i_location.logicalBlockNum;
+       } else {
+               block = iter->eloc.logicalBlockNum +
+                               ((iter->elen - 1) >> dir->i_blkbits);
+       }
+       off = iter->pos & (blksize - 1);
+       if (!off)
+               off = blksize;
+       /* Entry fits into current block? */
+       if (blksize - udf_ext0_offset(dir) - off >= nfidlen)
+               goto store_fi;
+
+       ret = udf_fiiter_append_blk(iter);
+       if (ret) {
+               udf_fiiter_release(iter);
+               return ret;
+       }
+
+       /* Entry will be completely in the new block? Update tag location... */
+       if (!(iter->pos & (blksize - 1)))
+               block = iter->eloc.logicalBlockNum +
+                               ((iter->elen - 1) >> dir->i_blkbits);
+store_fi:
+       memset(&iter->fi, 0, sizeof(struct fileIdentDesc));
+       if (UDF_SB(dir->i_sb)->s_udfrev >= 0x0200)
+               udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 3, 1, block,
+                           sizeof(struct tag));
+       else
+               udf_new_tag((char *)(&iter->fi), TAG_IDENT_FID, 2, 1, block,
+                           sizeof(struct tag));
+       iter->fi.fileVersionNum = cpu_to_le16(1);
+       iter->fi.lengthFileIdent = namelen;
+       iter->fi.lengthOfImpUse = cpu_to_le16(0);
+       memcpy(iter->namebuf, name, namelen);
+       iter->name = iter->namebuf;
+
+       dir->i_size += nfidlen;
+       if (dinfo->i_alloc_type == ICBTAG_FLAG_AD_IN_ICB) {
+               dinfo->i_lenAlloc += nfidlen;
+       } else {
+               /* Truncate last extent to proper size */
+               udf_fiiter_update_elen(iter, iter->elen -
+                                       (dinfo->i_lenExtents - dir->i_size));
+       }
+       mark_inode_dirty(dir);
+
+       return 0;
+}
+
 static struct fileIdentDesc *udf_add_entry(struct inode *dir,
                                           struct dentry *dentry,
                                           struct udf_fileident_bh *fibh,
index 676fa29..e47b2f0 100644 (file)
@@ -264,6 +264,8 @@ int udf_fiiter_init(struct udf_fileident_iter *iter, struct inode *dir,
 int udf_fiiter_advance(struct udf_fileident_iter *iter);
 void udf_fiiter_release(struct udf_fileident_iter *iter);
 void udf_fiiter_write_fi(struct udf_fileident_iter *iter, uint8_t *impuse);
+void udf_fiiter_update_elen(struct udf_fileident_iter *iter, uint32_t new_elen);
+int udf_fiiter_append_blk(struct udf_fileident_iter *iter);
 extern struct fileIdentDesc *udf_fileident_read(struct inode *, loff_t *,
                                                struct udf_fileident_bh *,
                                                struct fileIdentDesc *,