]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - fs/xfs/xfs_bmap_util.c
UBUNTU: Ubuntu-4.10.0-37.41
[mirror_ubuntu-zesty-kernel.git] / fs / xfs / xfs_bmap_util.c
index b9abce524c33b1af8581e8d52d032685fb767747..205ab55d595dd4155bfd10713d58dc3e5b5a1634 100644 (file)
@@ -528,7 +528,6 @@ xfs_getbmap(
        xfs_bmbt_irec_t         *map;           /* buffer for user's data */
        xfs_mount_t             *mp;            /* file system mount point */
        int                     nex;            /* # of user extents can do */
-       int                     nexleft;        /* # of user extents left */
        int                     subnex;         /* # of bmapi's can do */
        int                     nmap;           /* number of map entries */
        struct getbmapx         *out;           /* output structure */
@@ -686,10 +685,8 @@ xfs_getbmap(
                goto out_free_map;
        }
 
-       nexleft = nex;
-
        do {
-               nmap = (nexleft > subnex) ? subnex : nexleft;
+               nmap = (nex> subnex) ? subnex : nex;
                error = xfs_bmapi_read(ip, XFS_BB_TO_FSBT(mp, bmv->bmv_offset),
                                       XFS_BB_TO_FSB(mp, bmv->bmv_length),
                                       map, &nmap, bmapi_flags);
@@ -697,8 +694,8 @@ xfs_getbmap(
                        goto out_free_map;
                ASSERT(nmap <= subnex);
 
-               for (i = 0; i < nmap && nexleft && bmv->bmv_length &&
-                               cur_ext < bmv->bmv_count; i++) {
+               for (i = 0; i < nmap && bmv->bmv_length &&
+                               cur_ext < bmv->bmv_count - 1; i++) {
                        out[cur_ext].bmv_oflags = 0;
                        if (map[i].br_state == XFS_EXT_UNWRITTEN)
                                out[cur_ext].bmv_oflags |= BMV_OF_PREALLOC;
@@ -760,16 +757,27 @@ xfs_getbmap(
                                continue;
                        }
 
+                       /*
+                        * In order to report shared extents accurately,
+                        * we report each distinct shared/unshared part
+                        * of a single bmbt record using multiple bmap
+                        * extents.  To make that happen, we iterate the
+                        * same map array item multiple times, each
+                        * time trimming out the subextent that we just
+                        * reported.
+                        *
+                        * Because of this, we must check the out array
+                        * index (cur_ext) directly against bmv_count-1
+                        * to avoid overflows.
+                        */
                        if (inject_map.br_startblock != NULLFSBLOCK) {
                                map[i] = inject_map;
                                i--;
-                       } else
-                               nexleft--;
+                       }
                        bmv->bmv_entries++;
                        cur_ext++;
                }
-       } while (nmap && nexleft && bmv->bmv_length &&
-                cur_ext < bmv->bmv_count);
+       } while (nmap && bmv->bmv_length && cur_ext < bmv->bmv_count - 1);
 
  out_free_map:
        kmem_free(map);
@@ -909,17 +917,18 @@ xfs_can_free_eofblocks(struct xfs_inode *ip, bool force)
  */
 int
 xfs_free_eofblocks(
-       xfs_mount_t     *mp,
-       xfs_inode_t     *ip,
-       bool            need_iolock)
+       struct xfs_inode        *ip)
 {
-       xfs_trans_t     *tp;
-       int             error;
-       xfs_fileoff_t   end_fsb;
-       xfs_fileoff_t   last_fsb;
-       xfs_filblks_t   map_len;
-       int             nimaps;
-       xfs_bmbt_irec_t imap;
+       struct xfs_trans        *tp;
+       int                     error;
+       xfs_fileoff_t           end_fsb;
+       xfs_fileoff_t           last_fsb;
+       xfs_filblks_t           map_len;
+       int                     nimaps;
+       struct xfs_bmbt_irec    imap;
+       struct xfs_mount        *mp = ip->i_mount;
+
+       ASSERT(xfs_isilocked(ip, XFS_IOLOCK_EXCL));
 
        /*
         * Figure out if there are any blocks beyond the end
@@ -936,6 +945,10 @@ xfs_free_eofblocks(
        error = xfs_bmapi_read(ip, end_fsb, map_len, &imap, &nimaps, 0);
        xfs_iunlock(ip, XFS_ILOCK_SHARED);
 
+       /*
+        * If there are blocks after the end of file, truncate the file to its
+        * current size to free them up.
+        */
        if (!error && (nimaps != 0) &&
            (imap.br_startblock != HOLESTARTBLOCK ||
             ip->i_delayed_blks)) {
@@ -946,22 +959,13 @@ xfs_free_eofblocks(
                if (error)
                        return error;
 
-               /*
-                * There are blocks after the end of file.
-                * Free them up now by truncating the file to
-                * its current size.
-                */
-               if (need_iolock) {
-                       if (!xfs_ilock_nowait(ip, XFS_IOLOCK_EXCL))
-                               return -EAGAIN;
-               }
+               /* wait on dio to ensure i_size has settled */
+               inode_dio_wait(VFS_I(ip));
 
                error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0,
                                &tp);
                if (error) {
                        ASSERT(XFS_FORCED_SHUTDOWN(mp));
-                       if (need_iolock)
-                               xfs_iunlock(ip, XFS_IOLOCK_EXCL);
                        return error;
                }
 
@@ -989,8 +993,6 @@ xfs_free_eofblocks(
                }
 
                xfs_iunlock(ip, XFS_ILOCK_EXCL);
-               if (need_iolock)
-                       xfs_iunlock(ip, XFS_IOLOCK_EXCL);
        }
        return error;
 }
@@ -1316,8 +1318,16 @@ xfs_free_file_space(
        /*
         * Now that we've unmap all full blocks we'll have to zero out any
         * partial block at the beginning and/or end.  xfs_zero_range is
-        * smart enough to skip any holes, including those we just created.
+        * smart enough to skip any holes, including those we just created,
+        * but we must take care not to zero beyond EOF and enlarge i_size.
         */
+
+       if (offset >= XFS_ISIZE(ip))
+               return 0;
+
+       if (offset + len > XFS_ISIZE(ip))
+               len = XFS_ISIZE(ip) - offset;
+
        return xfs_zero_range(ip, offset, len, NULL);
 }
 
@@ -1385,10 +1395,16 @@ xfs_shift_file_space(
        xfs_fileoff_t           stop_fsb;
        xfs_fileoff_t           next_fsb;
        xfs_fileoff_t           shift_fsb;
+       uint                    resblks;
 
        ASSERT(direction == SHIFT_LEFT || direction == SHIFT_RIGHT);
 
        if (direction == SHIFT_LEFT) {
+               /*
+                * Reserve blocks to cover potential extent merges after left
+                * shift operations.
+                */
+               resblks = XFS_DIOSTRAT_SPACE_RES(mp, 0);
                next_fsb = XFS_B_TO_FSB(mp, offset + len);
                stop_fsb = XFS_B_TO_FSB(mp, VFS_I(ip)->i_size);
        } else {
@@ -1396,6 +1412,7 @@ xfs_shift_file_space(
                 * If right shift, delegate the work of initialization of
                 * next_fsb to xfs_bmap_shift_extent as it has ilock held.
                 */
+               resblks = 0;
                next_fsb = NULLFSBLOCK;
                stop_fsb = XFS_B_TO_FSB(mp, offset);
        }
@@ -1407,7 +1424,7 @@ xfs_shift_file_space(
         * into the accessible region of the file.
         */
        if (xfs_can_free_eofblocks(ip, true)) {
-               error = xfs_free_eofblocks(mp, ip, false);
+               error = xfs_free_eofblocks(ip);
                if (error)
                        return error;
        }
@@ -1437,21 +1454,14 @@ xfs_shift_file_space(
        }
 
        while (!error && !done) {
-               /*
-                * We would need to reserve permanent block for transaction.
-                * This will come into picture when after shifting extent into
-                * hole we found that adjacent extents can be merged which
-                * may lead to freeing of a block during record update.
-                */
-               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write,
-                               XFS_DIOSTRAT_SPACE_RES(mp, 0), 0, 0, &tp);
+               error = xfs_trans_alloc(mp, &M_RES(mp)->tr_write, resblks, 0, 0,
+                                       &tp);
                if (error)
                        break;
 
                xfs_ilock(ip, XFS_ILOCK_EXCL);
                error = xfs_trans_reserve_quota(tp, mp, ip->i_udquot,
-                               ip->i_gdquot, ip->i_pdquot,
-                               XFS_DIOSTRAT_SPACE_RES(mp, 0), 0,
+                               ip->i_gdquot, ip->i_pdquot, resblks, 0,
                                XFS_QMOPT_RES_REGBLKS);
                if (error)
                        goto out_trans_cancel;