|
@@ -50,7 +50,7 @@ struct strip_mine {
|
|
|
* @ip: the inode
|
|
|
* @dibh: the dinode buffer
|
|
|
* @block: the block number that was allocated
|
|
|
- * @private: any locked page held by the caller process
|
|
|
+ * @page: The (optional) page. This is looked up if @page is NULL
|
|
|
*
|
|
|
* Returns: errno
|
|
|
*/
|
|
@@ -109,8 +109,7 @@ static int gfs2_unstuffer_page(struct gfs2_inode *ip, struct buffer_head *dibh,
|
|
|
/**
|
|
|
* gfs2_unstuff_dinode - Unstuff a dinode when the data has grown too big
|
|
|
* @ip: The GFS2 inode to unstuff
|
|
|
- * @unstuffer: the routine that handles unstuffing a non-zero length file
|
|
|
- * @private: private data for the unstuffer
|
|
|
+ * @page: The (optional) page. This is looked up if the @page is NULL
|
|
|
*
|
|
|
* This routine unstuffs a dinode and returns it to a "normal" state such
|
|
|
* that the height can be grown in the traditional way.
|
|
@@ -884,84 +883,15 @@ out:
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * do_grow - Make a file look bigger than it is
|
|
|
- * @ip: the inode
|
|
|
- * @size: the size to set the file to
|
|
|
- *
|
|
|
- * Called with an exclusive lock on @ip.
|
|
|
- *
|
|
|
- * Returns: errno
|
|
|
- */
|
|
|
-
|
|
|
-static int do_grow(struct gfs2_inode *ip, u64 size)
|
|
|
-{
|
|
|
- struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
|
|
- struct gfs2_alloc *al;
|
|
|
- struct buffer_head *dibh;
|
|
|
- int error;
|
|
|
-
|
|
|
- al = gfs2_alloc_get(ip);
|
|
|
- if (!al)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- error = gfs2_quota_lock_check(ip);
|
|
|
- if (error)
|
|
|
- goto out;
|
|
|
-
|
|
|
- al->al_requested = sdp->sd_max_height + RES_DATA;
|
|
|
-
|
|
|
- error = gfs2_inplace_reserve(ip);
|
|
|
- if (error)
|
|
|
- goto out_gunlock_q;
|
|
|
-
|
|
|
- error = gfs2_trans_begin(sdp,
|
|
|
- sdp->sd_max_height + al->al_rgd->rd_length +
|
|
|
- RES_JDATA + RES_DINODE + RES_STATFS + RES_QUOTA, 0);
|
|
|
- if (error)
|
|
|
- goto out_ipres;
|
|
|
-
|
|
|
- error = gfs2_meta_inode_buffer(ip, &dibh);
|
|
|
- if (error)
|
|
|
- goto out_end_trans;
|
|
|
-
|
|
|
- if (size > sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)) {
|
|
|
- if (gfs2_is_stuffed(ip)) {
|
|
|
- error = gfs2_unstuff_dinode(ip, NULL);
|
|
|
- if (error)
|
|
|
- goto out_brelse;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- ip->i_disksize = size;
|
|
|
- ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
|
|
|
- gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
|
- gfs2_dinode_out(ip, dibh->b_data);
|
|
|
-
|
|
|
-out_brelse:
|
|
|
- brelse(dibh);
|
|
|
-out_end_trans:
|
|
|
- gfs2_trans_end(sdp);
|
|
|
-out_ipres:
|
|
|
- gfs2_inplace_release(ip);
|
|
|
-out_gunlock_q:
|
|
|
- gfs2_quota_unlock(ip);
|
|
|
-out:
|
|
|
- gfs2_alloc_put(ip);
|
|
|
- return error;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
/**
|
|
|
* gfs2_block_truncate_page - Deal with zeroing out data for truncate
|
|
|
*
|
|
|
* This is partly borrowed from ext3.
|
|
|
*/
|
|
|
-static int gfs2_block_truncate_page(struct address_space *mapping)
|
|
|
+static int gfs2_block_truncate_page(struct address_space *mapping, loff_t from)
|
|
|
{
|
|
|
struct inode *inode = mapping->host;
|
|
|
struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
- loff_t from = inode->i_size;
|
|
|
unsigned long index = from >> PAGE_CACHE_SHIFT;
|
|
|
unsigned offset = from & (PAGE_CACHE_SIZE-1);
|
|
|
unsigned blocksize, iblock, length, pos;
|
|
@@ -1023,9 +953,11 @@ unlock:
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
-static int trunc_start(struct gfs2_inode *ip, u64 size)
|
|
|
+static int trunc_start(struct inode *inode, u64 oldsize, u64 newsize)
|
|
|
{
|
|
|
- struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
|
|
+ struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
|
|
|
+ struct address_space *mapping = inode->i_mapping;
|
|
|
struct buffer_head *dibh;
|
|
|
int journaled = gfs2_is_jdata(ip);
|
|
|
int error;
|
|
@@ -1039,31 +971,27 @@ static int trunc_start(struct gfs2_inode *ip, u64 size)
|
|
|
if (error)
|
|
|
goto out;
|
|
|
|
|
|
+ gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
|
+
|
|
|
if (gfs2_is_stuffed(ip)) {
|
|
|
- u64 dsize = size + sizeof(struct gfs2_dinode);
|
|
|
- ip->i_disksize = size;
|
|
|
- ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
|
|
|
- gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
|
- gfs2_dinode_out(ip, dibh->b_data);
|
|
|
- if (dsize > dibh->b_size)
|
|
|
- dsize = dibh->b_size;
|
|
|
- gfs2_buffer_clear_tail(dibh, dsize);
|
|
|
- error = 1;
|
|
|
+ gfs2_buffer_clear_tail(dibh, sizeof(struct gfs2_dinode) + newsize);
|
|
|
} else {
|
|
|
- if (size & (u64)(sdp->sd_sb.sb_bsize - 1))
|
|
|
- error = gfs2_block_truncate_page(ip->i_inode.i_mapping);
|
|
|
-
|
|
|
- if (!error) {
|
|
|
- ip->i_disksize = size;
|
|
|
- ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
|
|
|
- ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG;
|
|
|
- gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
|
- gfs2_dinode_out(ip, dibh->b_data);
|
|
|
+ if (newsize & (u64)(sdp->sd_sb.sb_bsize - 1)) {
|
|
|
+ error = gfs2_block_truncate_page(mapping, newsize);
|
|
|
+ if (error)
|
|
|
+ goto out_brelse;
|
|
|
}
|
|
|
+ ip->i_diskflags |= GFS2_DIF_TRUNC_IN_PROG;
|
|
|
}
|
|
|
|
|
|
- brelse(dibh);
|
|
|
+ i_size_write(inode, newsize);
|
|
|
+ ip->i_disksize = newsize;
|
|
|
+ ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
|
|
|
+ gfs2_dinode_out(ip, dibh->b_data);
|
|
|
|
|
|
+ truncate_pagecache(inode, oldsize, newsize);
|
|
|
+out_brelse:
|
|
|
+ brelse(dibh);
|
|
|
out:
|
|
|
gfs2_trans_end(sdp);
|
|
|
return error;
|
|
@@ -1143,86 +1071,149 @@ out:
|
|
|
|
|
|
/**
|
|
|
* do_shrink - make a file smaller
|
|
|
- * @ip: the inode
|
|
|
- * @size: the size to make the file
|
|
|
- * @truncator: function to truncate the last partial block
|
|
|
+ * @inode: the inode
|
|
|
+ * @oldsize: the current inode size
|
|
|
+ * @newsize: the size to make the file
|
|
|
*
|
|
|
- * Called with an exclusive lock on @ip.
|
|
|
+ * Called with an exclusive lock on @inode. The @size must
|
|
|
+ * be equal to or smaller than the current inode size.
|
|
|
*
|
|
|
* Returns: errno
|
|
|
*/
|
|
|
|
|
|
-static int do_shrink(struct gfs2_inode *ip, u64 size)
|
|
|
+static int do_shrink(struct inode *inode, u64 oldsize, u64 newsize)
|
|
|
{
|
|
|
+ struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
int error;
|
|
|
|
|
|
- error = trunc_start(ip, size);
|
|
|
+ error = trunc_start(inode, oldsize, newsize);
|
|
|
if (error < 0)
|
|
|
return error;
|
|
|
- if (error > 0)
|
|
|
+ if (gfs2_is_stuffed(ip))
|
|
|
return 0;
|
|
|
|
|
|
- error = trunc_dealloc(ip, size);
|
|
|
- if (!error)
|
|
|
+ error = trunc_dealloc(ip, newsize);
|
|
|
+ if (error == 0)
|
|
|
error = trunc_end(ip);
|
|
|
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
-static int do_touch(struct gfs2_inode *ip, u64 size)
|
|
|
+void gfs2_trim_blocks(struct inode *inode)
|
|
|
{
|
|
|
- struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
|
|
|
+ u64 size = inode->i_size;
|
|
|
+ int ret;
|
|
|
+
|
|
|
+ ret = do_shrink(inode, size, size);
|
|
|
+ WARN_ON(ret != 0);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * do_grow - Touch and update inode size
|
|
|
+ * @inode: The inode
|
|
|
+ * @size: The new size
|
|
|
+ *
|
|
|
+ * This function updates the timestamps on the inode and
|
|
|
+ * may also increase the size of the inode. This function
|
|
|
+ * must not be called with @size any smaller than the current
|
|
|
+ * inode size.
|
|
|
+ *
|
|
|
+ * Although it is not strictly required to unstuff files here,
|
|
|
+ * earlier versions of GFS2 have a bug in the stuffed file reading
|
|
|
+ * code which will result in a buffer overrun if the size is larger
|
|
|
+ * than the max stuffed file size. In order to prevent this from
|
|
|
+ * occuring, such files are unstuffed, but in other cases we can
|
|
|
+ * just update the inode size directly.
|
|
|
+ *
|
|
|
+ * Returns: 0 on success, or -ve on error
|
|
|
+ */
|
|
|
+
|
|
|
+static int do_grow(struct inode *inode, u64 size)
|
|
|
+{
|
|
|
+ struct gfs2_inode *ip = GFS2_I(inode);
|
|
|
+ struct gfs2_sbd *sdp = GFS2_SB(inode);
|
|
|
struct buffer_head *dibh;
|
|
|
+ struct gfs2_alloc *al = NULL;
|
|
|
int error;
|
|
|
|
|
|
- error = gfs2_trans_begin(sdp, RES_DINODE, 0);
|
|
|
+ if (gfs2_is_stuffed(ip) &&
|
|
|
+ (size > (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_dinode)))) {
|
|
|
+ al = gfs2_alloc_get(ip);
|
|
|
+ if (al == NULL)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ error = gfs2_quota_lock_check(ip);
|
|
|
+ if (error)
|
|
|
+ goto do_grow_alloc_put;
|
|
|
+
|
|
|
+ al->al_requested = 1;
|
|
|
+ error = gfs2_inplace_reserve(ip);
|
|
|
+ if (error)
|
|
|
+ goto do_grow_qunlock;
|
|
|
+ }
|
|
|
+
|
|
|
+ error = gfs2_trans_begin(sdp, RES_DINODE + 1, 0);
|
|
|
if (error)
|
|
|
- return error;
|
|
|
+ goto do_grow_release;
|
|
|
|
|
|
- down_write(&ip->i_rw_mutex);
|
|
|
+ if (al) {
|
|
|
+ error = gfs2_unstuff_dinode(ip, NULL);
|
|
|
+ if (error)
|
|
|
+ goto do_end_trans;
|
|
|
+ }
|
|
|
|
|
|
error = gfs2_meta_inode_buffer(ip, &dibh);
|
|
|
if (error)
|
|
|
- goto do_touch_out;
|
|
|
+ goto do_end_trans;
|
|
|
|
|
|
+ i_size_write(inode, size);
|
|
|
+ ip->i_disksize = size;
|
|
|
ip->i_inode.i_mtime = ip->i_inode.i_ctime = CURRENT_TIME;
|
|
|
gfs2_trans_add_bh(ip->i_gl, dibh, 1);
|
|
|
gfs2_dinode_out(ip, dibh->b_data);
|
|
|
brelse(dibh);
|
|
|
|
|
|
-do_touch_out:
|
|
|
- up_write(&ip->i_rw_mutex);
|
|
|
+do_end_trans:
|
|
|
gfs2_trans_end(sdp);
|
|
|
+do_grow_release:
|
|
|
+ if (al) {
|
|
|
+ gfs2_inplace_release(ip);
|
|
|
+do_grow_qunlock:
|
|
|
+ gfs2_quota_unlock(ip);
|
|
|
+do_grow_alloc_put:
|
|
|
+ gfs2_alloc_put(ip);
|
|
|
+ }
|
|
|
return error;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * gfs2_truncatei - make a file a given size
|
|
|
- * @ip: the inode
|
|
|
- * @size: the size to make the file
|
|
|
- * @truncator: function to truncate the last partial block
|
|
|
+ * gfs2_setattr_size - make a file a given size
|
|
|
+ * @inode: the inode
|
|
|
+ * @newsize: the size to make the file
|
|
|
*
|
|
|
- * The file size can grow, shrink, or stay the same size.
|
|
|
+ * The file size can grow, shrink, or stay the same size. This
|
|
|
+ * is called holding i_mutex and an exclusive glock on the inode
|
|
|
+ * in question.
|
|
|
*
|
|
|
* Returns: errno
|
|
|
*/
|
|
|
|
|
|
-int gfs2_truncatei(struct gfs2_inode *ip, u64 size)
|
|
|
+int gfs2_setattr_size(struct inode *inode, u64 newsize)
|
|
|
{
|
|
|
- int error;
|
|
|
+ int ret;
|
|
|
+ u64 oldsize;
|
|
|
|
|
|
- if (gfs2_assert_warn(GFS2_SB(&ip->i_inode), S_ISREG(ip->i_inode.i_mode)))
|
|
|
- return -EINVAL;
|
|
|
+ BUG_ON(!S_ISREG(inode->i_mode));
|
|
|
|
|
|
- if (size > ip->i_disksize)
|
|
|
- error = do_grow(ip, size);
|
|
|
- else if (size < ip->i_disksize)
|
|
|
- error = do_shrink(ip, size);
|
|
|
- else
|
|
|
- /* update time stamps */
|
|
|
- error = do_touch(ip, size);
|
|
|
+ ret = inode_newsize_ok(inode, newsize);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- return error;
|
|
|
+ oldsize = inode->i_size;
|
|
|
+ if (newsize >= oldsize)
|
|
|
+ return do_grow(inode, newsize);
|
|
|
+
|
|
|
+ return do_shrink(inode, oldsize, newsize);
|
|
|
}
|
|
|
|
|
|
int gfs2_truncatei_resume(struct gfs2_inode *ip)
|