diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2016-06-29 16:03:55 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2016-06-29 16:03:55 +0200 |
commit | 03bea60409328de54e4ff7ec41672e12a9cb0908 (patch) | |
tree | e63b692eeb24c559c1e9b56369b18751a7bb9d5f /fs/overlayfs | |
parent | a4859d75944a726533ab86d24bb5ffd1b2b7d6cc (diff) | |
download | linux-03bea60409328de54e4ff7ec41672e12a9cb0908.tar.gz linux-03bea60409328de54e4ff7ec41672e12a9cb0908.tar.bz2 linux-03bea60409328de54e4ff7ec41672e12a9cb0908.zip |
ovl: get_write_access() in truncate
When truncating a file we should check write access on the underlying
inode. And we should do so on the lower file as well (before copy-up) for
consistency.
Original patch and test case by Aihua Zhang.
- - >o >o - - test.c - - >o >o - -
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ret;
ret = truncate(argv[0], 4096);
if (ret != -1) {
fprintf(stderr, "truncate(argv[0]) should have failed\n");
return 1;
}
if (errno != ETXTBSY) {
perror("truncate(argv[0])");
return 1;
}
return 0;
}
- - >o >o - - >o >o - - >o >o - -
Reported-by: Aihua Zhang <zhangaihua1@huawei.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Cc: <stable@vger.kernel.org>
Diffstat (limited to 'fs/overlayfs')
-rw-r--r-- | fs/overlayfs/inode.c | 21 |
1 files changed, 21 insertions, 0 deletions
diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 8514d692042b..c831c2e5f803 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -59,16 +59,37 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) if (err) goto out; + if (attr->ia_valid & ATTR_SIZE) { + struct inode *realinode = d_inode(ovl_dentry_real(dentry)); + + err = -ETXTBSY; + if (atomic_read(&realinode->i_writecount) < 0) + goto out_drop_write; + } + err = ovl_copy_up(dentry); if (!err) { + struct inode *winode = NULL; + upperdentry = ovl_dentry_upper(dentry); + if (attr->ia_valid & ATTR_SIZE) { + winode = d_inode(upperdentry); + err = get_write_access(winode); + if (err) + goto out_drop_write; + } + inode_lock(upperdentry->d_inode); err = notify_change(upperdentry, attr, NULL); if (!err) ovl_copyattr(upperdentry->d_inode, dentry->d_inode); inode_unlock(upperdentry->d_inode); + + if (winode) + put_write_access(winode); } +out_drop_write: ovl_drop_write(dentry); out: return err; |