Merge tag 'for_v5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jul 2019 03:27:07 +0000 (20:27 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jul 2019 03:27:07 +0000 (20:27 -0700)
Pull ext2, udf and quota updates from Jan Kara:

 - some ext2 fixes and cleanups

 - a fix of udf bug when extending files

 - a fix of quota Q_XGETQSTAT[V] handling

* tag 'for_v5.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  udf: Fix incorrect final NOT_ALLOCATED (hole) extent length
  ext2: Use kmemdup rather than duplicating its implementation
  quota: honor quota type in Q_XGETQSTAT[V] calls
  ext2: Always brelse bh on failure in ext2_iget()
  ext2: add missing brelse() in ext2_iget()
  ext2: Fix a typo in ext2_getattr argument
  ext2: fix a typo in comment
  ext2: add missing brelse() in ext2_new_inode()
  ext2: optimize ext2_xattr_get()
  ext2: introduce new helper for xattr entry comparison
  ext2: merge xattr next entry check to ext2_xattr_entry_valid()
  ext2: code cleanup for ext2_preread_inode()
  ext2: code cleanup by using test_opt() and clear_opt()
  doc: ext2: update description of quota options for ext2
  ext2: Strengthen xattr block checks
  ext2: Merge loops in ext2_xattr_set()
  ext2: introduce helper for xattr entry validation
  ext2: introduce helper for xattr header validation
  quota: add dqi_dirty_list description to comment of Dquot List Management

Documentation/filesystems/ext2.txt
fs/ext2/balloc.c
fs/ext2/ialloc.c
fs/ext2/inode.c
fs/ext2/super.c
fs/ext2/xattr.c
fs/quota/dquot.c
fs/quota/quota.c
fs/udf/inode.c

index a19973a4dd1e89fa9f3a6381b6a33945e39ef111..94c2cf0292f584837651bdb2374740aea6473e08 100644 (file)
@@ -57,7 +57,13 @@ noacl                                Don't support POSIX ACLs.
 
 nobh                           Do not attach buffer_heads to file pagecache.
 
-grpquota,noquota,quota,usrquota        Quota options are silently ignored by ext2.
+quota, usrquota                        Enable user disk quota support
+                               (requires CONFIG_QUOTA).
+
+grpquota                       Enable group disk quota support
+                               (requires CONFIG_QUOTA).
+
+noquota option ls silently ignored by ext2.
 
 
 Specification
index 33db13365c5eb8c52265218a327f302dbac2fed5..547c165299c0c5a209cd6f5f45d2e31e5e3f364e 100644 (file)
@@ -1197,7 +1197,7 @@ static int ext2_has_free_blocks(struct ext2_sb_info *sbi)
 
 /*
  * Returns 1 if the passed-in block region is valid; 0 if some part overlaps
- * with filesystem metadata blocksi.
+ * with filesystem metadata blocks.
  */
 int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
                          unsigned int count)
@@ -1212,7 +1212,6 @@ int ext2_data_block_valid(struct ext2_sb_info *sbi, ext2_fsblk_t start_blk,
            (start_blk + count >= sbi->s_sb_block))
                return 0;
 
-
        return 1;
 }
 
index a0c5ea91fcd499d8676301eb578147dcd06a56f0..fda7d3f5b4be53161b725a0b6b8a5660027242a7 100644 (file)
@@ -172,9 +172,7 @@ static void ext2_preread_inode(struct inode *inode)
        struct backing_dev_info *bdi;
 
        bdi = inode_to_bdi(inode);
-       if (bdi_read_congested(bdi))
-               return;
-       if (bdi_write_congested(bdi))
+       if (bdi_rw_congested(bdi))
                return;
 
        block_group = (inode->i_ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
@@ -511,6 +509,7 @@ repeat_in_this_group:
        /*
         * Scanned all blockgroups.
         */
+       brelse(bitmap_bh);
        err = -ENOSPC;
        goto fail;
 got:
index e474127dd255605df4ab498997111ac1cf63fafa..7004ce581a328b29eebae9e28e220c9fe5c7bbbb 100644 (file)
@@ -1400,7 +1400,7 @@ void ext2_set_file_ops(struct inode *inode)
 struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
 {
        struct ext2_inode_info *ei;
-       struct buffer_head * bh;
+       struct buffer_head * bh = NULL;
        struct ext2_inode *raw_inode;
        struct inode *inode;
        long ret = -EIO;
@@ -1446,7 +1446,6 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
         */
        if (inode->i_nlink == 0 && (inode->i_mode == 0 || ei->i_dtime)) {
                /* this inode is deleted */
-               brelse (bh);
                ret = -ESTALE;
                goto bad_inode;
        }
@@ -1463,7 +1462,6 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
            !ext2_data_block_valid(EXT2_SB(sb), ei->i_file_acl, 1)) {
                ext2_error(sb, "ext2_iget", "bad extended attribute block %u",
                           ei->i_file_acl);
-               brelse(bh);
                ret = -EFSCORRUPTED;
                goto bad_inode;
        }
@@ -1526,6 +1524,7 @@ struct inode *ext2_iget (struct super_block *sb, unsigned long ino)
        return inode;
        
 bad_inode:
+       brelse(bh);
        iget_failed(inode);
        return ERR_PTR(ret);
 }
@@ -1640,7 +1639,7 @@ int ext2_write_inode(struct inode *inode, struct writeback_control *wbc)
 }
 
 int ext2_getattr(const struct path *path, struct kstat *stat,
-               u32 request_mask, unsigned int query_falgs)
+               u32 request_mask, unsigned int query_flags)
 {
        struct inode *inode = d_inode(path->dentry);
        struct ext2_inode_info *ei = EXT2_I(inode);
index 1d7ab73b10148448e07c905b561c0e95087ba608..44eb6e7eb492f2fc302cbe0652fd83269e7d2950 100644 (file)
@@ -303,16 +303,16 @@ static int ext2_show_options(struct seq_file *seq, struct dentry *root)
        if (test_opt(sb, NOBH))
                seq_puts(seq, ",nobh");
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_USRQUOTA)
+       if (test_opt(sb, USRQUOTA))
                seq_puts(seq, ",usrquota");
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_GRPQUOTA)
+       if (test_opt(sb, GRPQUOTA))
                seq_puts(seq, ",grpquota");
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_XIP)
+       if (test_opt(sb, XIP))
                seq_puts(seq, ",xip");
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_DAX)
+       if (test_opt(sb, DAX))
                seq_puts(seq, ",dax");
 
        if (!test_opt(sb, RESERVATION))
@@ -935,8 +935,7 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
        sbi->s_resgid = opts.s_resgid;
 
        sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
-               ((EXT2_SB(sb)->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ?
-                SB_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
        sb->s_iflags |= SB_I_CGROUPWB;
 
        if (le32_to_cpu(es->s_rev_level) == EXT2_GOOD_OLD_REV &&
@@ -967,11 +966,11 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
 
        blocksize = BLOCK_SIZE << le32_to_cpu(sbi->s_es->s_log_block_size);
 
-       if (sbi->s_mount_opt & EXT2_MOUNT_DAX) {
+       if (test_opt(sb, DAX)) {
                if (!bdev_dax_supported(sb->s_bdev, blocksize)) {
                        ext2_msg(sb, KERN_ERR,
                                "DAX unsupported by block device. Turning off DAX.");
-                       sbi->s_mount_opt &= ~EXT2_MOUNT_DAX;
+                       clear_opt(sbi->s_mount_opt, DAX);
                }
        }
 
@@ -1404,7 +1403,7 @@ out_set:
        sbi->s_resuid = new_opts.s_resuid;
        sbi->s_resgid = new_opts.s_resgid;
        sb->s_flags = (sb->s_flags & ~SB_POSIXACL) |
-               ((sbi->s_mount_opt & EXT2_MOUNT_POSIX_ACL) ? SB_POSIXACL : 0);
+               (test_opt(sb, POSIX_ACL) ? SB_POSIXACL : 0);
        spin_unlock(&sbi->s_lock);
 
        return 0;
index 1e33e0ac8cf1f8dbbaa8e364f5a6ca08a24d84ef..79369c13cc55780d06ef382940e4b2c029212a38 100644 (file)
@@ -134,6 +134,53 @@ ext2_xattr_handler(int name_index)
        return handler;
 }
 
+static bool
+ext2_xattr_header_valid(struct ext2_xattr_header *header)
+{
+       if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
+           header->h_blocks != cpu_to_le32(1))
+               return false;
+
+       return true;
+}
+
+static bool
+ext2_xattr_entry_valid(struct ext2_xattr_entry *entry,
+                      char *end, size_t end_offs)
+{
+       struct ext2_xattr_entry *next;
+       size_t size;
+
+       next = EXT2_XATTR_NEXT(entry);
+       if ((char *)next >= end)
+               return false;
+
+       if (entry->e_value_block != 0)
+               return false;
+
+       size = le32_to_cpu(entry->e_value_size);
+       if (size > end_offs ||
+           le16_to_cpu(entry->e_value_offs) + size > end_offs)
+               return false;
+
+       return true;
+}
+
+static int
+ext2_xattr_cmp_entry(int name_index, size_t name_len, const char *name,
+                    struct ext2_xattr_entry *entry)
+{
+       int cmp;
+
+       cmp = name_index - entry->e_name_index;
+       if (!cmp)
+               cmp = name_len - entry->e_name_len;
+       if (!cmp)
+               cmp = memcmp(name, entry->e_name, name_len);
+
+       return cmp;
+}
+
 /*
  * ext2_xattr_get()
  *
@@ -152,7 +199,7 @@ ext2_xattr_get(struct inode *inode, int name_index, const char *name,
        struct ext2_xattr_entry *entry;
        size_t name_len, size;
        char *end;
-       int error;
+       int error, not_found;
        struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode);
 
        ea_idebug(inode, "name=%d.%s, buffer=%p, buffer_size=%ld",
@@ -176,9 +223,9 @@ ext2_xattr_get(struct inode *inode, int name_index, const char *name,
        ea_bdebug(bh, "b_count=%d, refcount=%d",
                atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
        end = bh->b_data + bh->b_size;
-       if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
-           HDR(bh)->h_blocks != cpu_to_le32(1)) {
-bad_block:     ext2_error(inode->i_sb, "ext2_xattr_get",
+       if (!ext2_xattr_header_valid(HDR(bh))) {
+bad_block:
+               ext2_error(inode->i_sb, "ext2_xattr_get",
                        "inode %ld: bad block %d", inode->i_ino,
                        EXT2_I(inode)->i_file_acl);
                error = -EIO;
@@ -188,29 +235,25 @@ bad_block:        ext2_error(inode->i_sb, "ext2_xattr_get",
        /* find named attribute */
        entry = FIRST_ENTRY(bh);
        while (!IS_LAST_ENTRY(entry)) {
-               struct ext2_xattr_entry *next =
-                       EXT2_XATTR_NEXT(entry);
-               if ((char *)next >= end)
+               if (!ext2_xattr_entry_valid(entry, end,
+                   inode->i_sb->s_blocksize))
                        goto bad_block;
-               if (name_index == entry->e_name_index &&
-                   name_len == entry->e_name_len &&
-                   memcmp(name, entry->e_name, name_len) == 0)
+
+               not_found = ext2_xattr_cmp_entry(name_index, name_len, name,
+                                                entry);
+               if (!not_found)
                        goto found;
-               entry = next;
+               if (not_found < 0)
+                       break;
+
+               entry = EXT2_XATTR_NEXT(entry);
        }
        if (ext2_xattr_cache_insert(ea_block_cache, bh))
                ea_idebug(inode, "cache insert failed");
        error = -ENODATA;
        goto cleanup;
 found:
-       /* check the buffer size */
-       if (entry->e_value_block != 0)
-               goto bad_block;
        size = le32_to_cpu(entry->e_value_size);
-       if (size > inode->i_sb->s_blocksize ||
-           le16_to_cpu(entry->e_value_offs) + size > inode->i_sb->s_blocksize)
-               goto bad_block;
-
        if (ext2_xattr_cache_insert(ea_block_cache, bh))
                ea_idebug(inode, "cache insert failed");
        if (buffer) {
@@ -266,9 +309,9 @@ ext2_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
        ea_bdebug(bh, "b_count=%d, refcount=%d",
                atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
        end = bh->b_data + bh->b_size;
-       if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
-           HDR(bh)->h_blocks != cpu_to_le32(1)) {
-bad_block:     ext2_error(inode->i_sb, "ext2_xattr_list",
+       if (!ext2_xattr_header_valid(HDR(bh))) {
+bad_block:
+               ext2_error(inode->i_sb, "ext2_xattr_list",
                        "inode %ld: bad block %d", inode->i_ino,
                        EXT2_I(inode)->i_file_acl);
                error = -EIO;
@@ -278,11 +321,10 @@ bad_block:        ext2_error(inode->i_sb, "ext2_xattr_list",
        /* check the on-disk data structure */
        entry = FIRST_ENTRY(bh);
        while (!IS_LAST_ENTRY(entry)) {
-               struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(entry);
-
-               if ((char *)next >= end)
+               if (!ext2_xattr_entry_valid(entry, end,
+                   inode->i_sb->s_blocksize))
                        goto bad_block;
-               entry = next;
+               entry = EXT2_XATTR_NEXT(entry);
        }
        if (ext2_xattr_cache_insert(ea_block_cache, bh))
                ea_idebug(inode, "cache insert failed");
@@ -367,7 +409,7 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
        struct super_block *sb = inode->i_sb;
        struct buffer_head *bh = NULL;
        struct ext2_xattr_header *header = NULL;
-       struct ext2_xattr_entry *here, *last;
+       struct ext2_xattr_entry *here = NULL, *last = NULL;
        size_t name_len, free, min_offs = sb->s_blocksize;
        int not_found = 1, error;
        char *end;
@@ -406,47 +448,39 @@ ext2_xattr_set(struct inode *inode, int name_index, const char *name,
                        le32_to_cpu(HDR(bh)->h_refcount));
                header = HDR(bh);
                end = bh->b_data + bh->b_size;
-               if (header->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
-                   header->h_blocks != cpu_to_le32(1)) {
-bad_block:             ext2_error(sb, "ext2_xattr_set",
+               if (!ext2_xattr_header_valid(header)) {
+bad_block:
+                       ext2_error(sb, "ext2_xattr_set",
                                "inode %ld: bad block %d", inode->i_ino, 
                                   EXT2_I(inode)->i_file_acl);
                        error = -EIO;
                        goto cleanup;
                }
-               /* Find the named attribute. */
-               here = FIRST_ENTRY(bh);
-               while (!IS_LAST_ENTRY(here)) {
-                       struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(here);
-                       if ((char *)next >= end)
-                               goto bad_block;
-                       if (!here->e_value_block && here->e_value_size) {
-                               size_t offs = le16_to_cpu(here->e_value_offs);
-                               if (offs < min_offs)
-                                       min_offs = offs;
-                       }
-                       not_found = name_index - here->e_name_index;
-                       if (!not_found)
-                               not_found = name_len - here->e_name_len;
-                       if (!not_found)
-                               not_found = memcmp(name, here->e_name,name_len);
-                       if (not_found <= 0)
-                               break;
-                       here = next;
-               }
-               last = here;
-               /* We still need to compute min_offs and last. */
+               /*
+                * Find the named attribute. If not found, 'here' will point
+                * to entry where the new attribute should be inserted to
+                * maintain sorting.
+                */
+               last = FIRST_ENTRY(bh);
                while (!IS_LAST_ENTRY(last)) {
-                       struct ext2_xattr_entry *next = EXT2_XATTR_NEXT(last);
-                       if ((char *)next >= end)
+                       if (!ext2_xattr_entry_valid(last, end, sb->s_blocksize))
                                goto bad_block;
-                       if (!last->e_value_block && last->e_value_size) {
+                       if (last->e_value_size) {
                                size_t offs = le16_to_cpu(last->e_value_offs);
                                if (offs < min_offs)
                                        min_offs = offs;
                        }
-                       last = next;
+                       if (not_found > 0) {
+                               not_found = ext2_xattr_cmp_entry(name_index,
+                                                                name_len,
+                                                                name, last);
+                               if (not_found <= 0)
+                                       here = last;
+                       }
+                       last = EXT2_XATTR_NEXT(last);
                }
+               if (not_found > 0)
+                       here = last;
 
                /* Check whether we have enough space left. */
                free = min_offs - ((char*)last - (char*)header) - sizeof(__u32);
@@ -454,7 +488,6 @@ bad_block:          ext2_error(sb, "ext2_xattr_set",
                /* We will use a new extended attribute block. */
                free = sb->s_blocksize -
                        sizeof(struct ext2_xattr_header) - sizeof(__u32);
-               here = last = NULL;  /* avoid gcc uninitialized warning. */
        }
 
        if (not_found) {
@@ -470,14 +503,7 @@ bad_block:         ext2_error(sb, "ext2_xattr_set",
                error = -EEXIST;
                if (flags & XATTR_CREATE)
                        goto cleanup;
-               if (!here->e_value_block && here->e_value_size) {
-                       size_t size = le32_to_cpu(here->e_value_size);
-
-                       if (le16_to_cpu(here->e_value_offs) + size > 
-                           sb->s_blocksize || size > sb->s_blocksize)
-                               goto bad_block;
-                       free += EXT2_XATTR_SIZE(size);
-               }
+               free += EXT2_XATTR_SIZE(le32_to_cpu(here->e_value_size));
                free += EXT2_XATTR_LEN(name_len);
        }
        error = -ENOSPC;
@@ -506,11 +532,10 @@ bad_block:                ext2_error(sb, "ext2_xattr_set",
 
                        unlock_buffer(bh);
                        ea_bdebug(bh, "cloning");
-                       header = kmalloc(bh->b_size, GFP_KERNEL);
+                       header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL);
                        error = -ENOMEM;
                        if (header == NULL)
                                goto cleanup;
-                       memcpy(header, HDR(bh), bh->b_size);
                        header->h_refcount = cpu_to_le32(1);
 
                        offset = (char *)here - bh->b_data;
@@ -542,7 +567,7 @@ bad_block:          ext2_error(sb, "ext2_xattr_set",
                here->e_name_len = name_len;
                memcpy(here->e_name, name, name_len);
        } else {
-               if (!here->e_value_block && here->e_value_size) {
+               if (here->e_value_size) {
                        char *first_val = (char *)header + min_offs;
                        size_t offs = le16_to_cpu(here->e_value_offs);
                        char *val = (char *)header + offs;
@@ -569,7 +594,7 @@ bad_block:          ext2_error(sb, "ext2_xattr_set",
                        last = ENTRY(header+1);
                        while (!IS_LAST_ENTRY(last)) {
                                size_t o = le16_to_cpu(last->e_value_offs);
-                               if (!last->e_value_block && o < offs)
+                               if (o < offs)
                                        last->e_value_offs =
                                                cpu_to_le16(o + size);
                                last = EXT2_XATTR_NEXT(last);
@@ -784,8 +809,7 @@ ext2_xattr_delete_inode(struct inode *inode)
                goto cleanup;
        }
        ea_bdebug(bh, "b_count=%d", atomic_read(&(bh->b_count)));
-       if (HDR(bh)->h_magic != cpu_to_le32(EXT2_XATTR_MAGIC) ||
-           HDR(bh)->h_blocks != cpu_to_le32(1)) {
+       if (!ext2_xattr_header_valid(HDR(bh))) {
                ext2_error(inode->i_sb, "ext2_xattr_delete_inode",
                        "inode %ld: bad block %d", inode->i_ino,
                        EXT2_I(inode)->i_file_acl);
index 58f15a083dd11d4f945be0c05f40437d103d6288..be9c471cdbc8c6edc7e6ffbfd0fe33f48684ca37 100644 (file)
@@ -223,9 +223,9 @@ static void put_quota_format(struct quota_format_type *fmt)
 
 /*
  * Dquot List Management:
- * The quota code uses three lists for dquot management: the inuse_list,
- * free_dquots, and dquot_hash[] array. A single dquot structure may be
- * on all three lists, depending on its current state.
+ * The quota code uses four lists for dquot management: the inuse_list,
+ * free_dquots, dqi_dirty_list, and dquot_hash[] array. A single dquot
+ * structure may be on some of those lists, depending on its current state.
  *
  * All dquots are placed to the end of inuse_list when first created, and this
  * list is used for invalidate operation, which must look at every dquot.
@@ -236,6 +236,11 @@ static void put_quota_format(struct quota_format_type *fmt)
  * dqstats.free_dquots gives the number of dquots on the list. When
  * dquot is invalidated it's completely released from memory.
  *
+ * Dirty dquots are added to the dqi_dirty_list of quota_info when mark
+ * dirtied, and this list is searched when writing dirty dquots back to
+ * quota file. Note that some filesystems do dirty dquot tracking on their
+ * own (e.g. in a journal) and thus don't use dqi_dirty_list.
+ *
  * Dquots with a specific identity (device, type and id) are placed on
  * one of the dquot_hash[] hash chains. The provides an efficient search
  * mechanism to locate a specific dquot.
index fd5dd806f1b9223d09f4ad94d0d083fca54e583f..cb13fb76dbee5416c0185f2d4e1038138be0784b 100644 (file)
@@ -331,9 +331,9 @@ static int quota_state_to_flags(struct qc_state *state)
        return flags;
 }
 
-static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs)
+static int quota_getstate(struct super_block *sb, int type,
+                         struct fs_quota_stat *fqs)
 {
-       int type;
        struct qc_state state;
        int ret;
 
@@ -349,14 +349,7 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs)
        if (!fqs->qs_flags)
                return -ENOSYS;
        fqs->qs_incoredqs = state.s_incoredqs;
-       /*
-        * GETXSTATE quotactl has space for just one set of time limits so
-        * report them for the first enabled quota type
-        */
-       for (type = 0; type < MAXQUOTAS; type++)
-               if (state.s_state[type].flags & QCI_ACCT_ENABLED)
-                       break;
-       BUG_ON(type == MAXQUOTAS);
+
        fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
        fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
        fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
@@ -391,22 +384,22 @@ static int quota_getstate(struct super_block *sb, struct fs_quota_stat *fqs)
        return 0;
 }
 
-static int quota_getxstate(struct super_block *sb, void __user *addr)
+static int quota_getxstate(struct super_block *sb, int type, void __user *addr)
 {
        struct fs_quota_stat fqs;
        int ret;
 
        if (!sb->s_qcop->get_state)
                return -ENOSYS;
-       ret = quota_getstate(sb, &fqs);
+       ret = quota_getstate(sb, type, &fqs);
        if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
                return -EFAULT;
        return ret;
 }
 
-static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs)
+static int quota_getstatev(struct super_block *sb, int type,
+                          struct fs_quota_statv *fqs)
 {
-       int type;
        struct qc_state state;
        int ret;
 
@@ -422,14 +415,7 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs)
        if (!fqs->qs_flags)
                return -ENOSYS;
        fqs->qs_incoredqs = state.s_incoredqs;
-       /*
-        * GETXSTATV quotactl has space for just one set of time limits so
-        * report them for the first enabled quota type
-        */
-       for (type = 0; type < MAXQUOTAS; type++)
-               if (state.s_state[type].flags & QCI_ACCT_ENABLED)
-                       break;
-       BUG_ON(type == MAXQUOTAS);
+
        fqs->qs_btimelimit = state.s_state[type].spc_timelimit;
        fqs->qs_itimelimit = state.s_state[type].ino_timelimit;
        fqs->qs_rtbtimelimit = state.s_state[type].rt_spc_timelimit;
@@ -455,7 +441,7 @@ static int quota_getstatev(struct super_block *sb, struct fs_quota_statv *fqs)
        return 0;
 }
 
-static int quota_getxstatev(struct super_block *sb, void __user *addr)
+static int quota_getxstatev(struct super_block *sb, int type, void __user *addr)
 {
        struct fs_quota_statv fqs;
        int ret;
@@ -474,7 +460,7 @@ static int quota_getxstatev(struct super_block *sb, void __user *addr)
        default:
                return -EINVAL;
        }
-       ret = quota_getstatev(sb, &fqs);
+       ret = quota_getstatev(sb, type, &fqs);
        if (!ret && copy_to_user(addr, &fqs, sizeof(fqs)))
                return -EFAULT;
        return ret;
@@ -744,9 +730,9 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id,
        case Q_XQUOTARM:
                return quota_rmxquota(sb, addr);
        case Q_XGETQSTAT:
-               return quota_getxstate(sb, addr);
+               return quota_getxstate(sb, type, addr);
        case Q_XGETQSTATV:
-               return quota_getxstatev(sb, addr);
+               return quota_getxstatev(sb, type, addr);
        case Q_XSETQLIM:
                return quota_setxquota(sb, type, id, addr);
        case Q_XGETQUOTA:
index e7276932e433c9cdc1bdd10f9a35a3d0ef09ece7..9bb18311a22fc1a60bb642c8b68891c2e5a05949 100644 (file)
@@ -470,13 +470,15 @@ static struct buffer_head *udf_getblk(struct inode *inode, udf_pblk_t block,
        return NULL;
 }
 
-/* Extend the file by 'blocks' blocks, return the number of extents added */
+/* Extend the file with new blocks totaling 'new_block_bytes',
+ * return the number of extents added
+ */
 static int udf_do_extend_file(struct inode *inode,
                              struct extent_position *last_pos,
                              struct kernel_long_ad *last_ext,
-                             sector_t blocks)
+                             loff_t new_block_bytes)
 {
-       sector_t add;
+       uint32_t add;
        int count = 0, fake = !(last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
        struct super_block *sb = inode->i_sb;
        struct kernel_lb_addr prealloc_loc = {};
@@ -486,7 +488,7 @@ static int udf_do_extend_file(struct inode *inode,
 
        /* The previous extent is fake and we should not extend by anything
         * - there's nothing to do... */
-       if (!blocks && fake)
+       if (!new_block_bytes && fake)
                return 0;
 
        iinfo = UDF_I(inode);
@@ -517,13 +519,12 @@ static int udf_do_extend_file(struct inode *inode,
        /* Can we merge with the previous extent? */
        if ((last_ext->extLength & UDF_EXTENT_FLAG_MASK) ==
                                        EXT_NOT_RECORDED_NOT_ALLOCATED) {
-               add = ((1 << 30) - sb->s_blocksize -
-                       (last_ext->extLength & UDF_EXTENT_LENGTH_MASK)) >>
-                       sb->s_blocksize_bits;
-               if (add > blocks)
-                       add = blocks;
-               blocks -= add;
-               last_ext->extLength += add << sb->s_blocksize_bits;
+               add = (1 << 30) - sb->s_blocksize -
+                       (last_ext->extLength & UDF_EXTENT_LENGTH_MASK);
+               if (add > new_block_bytes)
+                       add = new_block_bytes;
+               new_block_bytes -= add;
+               last_ext->extLength += add;
        }
 
        if (fake) {
@@ -544,28 +545,27 @@ static int udf_do_extend_file(struct inode *inode,
        }
 
        /* Managed to do everything necessary? */
-       if (!blocks)
+       if (!new_block_bytes)
                goto out;
 
        /* All further extents will be NOT_RECORDED_NOT_ALLOCATED */
        last_ext->extLocation.logicalBlockNum = 0;
        last_ext->extLocation.partitionReferenceNum = 0;
-       add = (1 << (30-sb->s_blocksize_bits)) - 1;
-       last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-                               (add << sb->s_blocksize_bits);
+       add = (1 << 30) - sb->s_blocksize;
+       last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED | add;
 
        /* Create enough extents to cover the whole hole */
-       while (blocks > add) {
-               blocks -= add;
+       while (new_block_bytes > add) {
+               new_block_bytes -= add;
                err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
                                   last_ext->extLength, 1);
                if (err)
                        return err;
                count++;
        }
-       if (blocks) {
+       if (new_block_bytes) {
                last_ext->extLength = EXT_NOT_RECORDED_NOT_ALLOCATED |
-                       (blocks << sb->s_blocksize_bits);
+                       new_block_bytes;
                err = udf_add_aext(inode, last_pos, &last_ext->extLocation,
                                   last_ext->extLength, 1);
                if (err)
@@ -596,6 +596,24 @@ out:
        return count;
 }
 
+/* Extend the final block of the file to final_block_len bytes */
+static void udf_do_extend_final_block(struct inode *inode,
+                                     struct extent_position *last_pos,
+                                     struct kernel_long_ad *last_ext,
+                                     uint32_t final_block_len)
+{
+       struct super_block *sb = inode->i_sb;
+       uint32_t added_bytes;
+
+       added_bytes = final_block_len -
+                     (last_ext->extLength & (sb->s_blocksize - 1));
+       last_ext->extLength += added_bytes;
+       UDF_I(inode)->i_lenExtents += added_bytes;
+
+       udf_write_aext(inode, last_pos, &last_ext->extLocation,
+                       last_ext->extLength, 1);
+}
+
 static int udf_extend_file(struct inode *inode, loff_t newsize)
 {
 
@@ -605,10 +623,12 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
        int8_t etype;
        struct super_block *sb = inode->i_sb;
        sector_t first_block = newsize >> sb->s_blocksize_bits, offset;
+       unsigned long partial_final_block;
        int adsize;
        struct udf_inode_info *iinfo = UDF_I(inode);
        struct kernel_long_ad extent;
-       int err;
+       int err = 0;
+       int within_final_block;
 
        if (iinfo->i_alloc_type == ICBTAG_FLAG_AD_SHORT)
                adsize = sizeof(struct short_ad);
@@ -618,18 +638,8 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
                BUG();
 
        etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
+       within_final_block = (etype != -1);
 
-       /* File has extent covering the new size (could happen when extending
-        * inside a block)? */
-       if (etype != -1)
-               return 0;
-       if (newsize & (sb->s_blocksize - 1))
-               offset++;
-       /* Extended file just to the boundary of the last file block? */
-       if (offset == 0)
-               return 0;
-
-       /* Truncate is extending the file by 'offset' blocks */
        if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
            (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
                /* File has no extents at all or has empty last
@@ -643,7 +653,22 @@ static int udf_extend_file(struct inode *inode, loff_t newsize)
                                      &extent.extLength, 0);
                extent.extLength |= etype << 30;
        }
-       err = udf_do_extend_file(inode, &epos, &extent, offset);
+
+       partial_final_block = newsize & (sb->s_blocksize - 1);
+
+       /* File has extent covering the new size (could happen when extending
+        * inside a block)?
+        */
+       if (within_final_block) {
+               /* Extending file within the last file block */
+               udf_do_extend_final_block(inode, &epos, &extent,
+                                         partial_final_block);
+       } else {
+               loff_t add = ((loff_t)offset << sb->s_blocksize_bits) |
+                            partial_final_block;
+               err = udf_do_extend_file(inode, &epos, &extent, add);
+       }
+
        if (err < 0)
                goto out;
        err = 0;
@@ -745,6 +770,7 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
        /* Are we beyond EOF? */
        if (etype == -1) {
                int ret;
+               loff_t hole_len;
                isBeyondEOF = true;
                if (count) {
                        if (c)
@@ -760,7 +786,8 @@ static sector_t inode_getblk(struct inode *inode, sector_t block,
                        startnum = (offset > 0);
                }
                /* Create extents for the hole between EOF and offset */
-               ret = udf_do_extend_file(inode, &prev_epos, laarr, offset);
+               hole_len = (loff_t)offset << inode->i_blkbits;
+               ret = udf_do_extend_file(inode, &prev_epos, laarr, hole_len);
                if (ret < 0) {
                        *err = ret;
                        newblock = 0;