Commit 12b9fa6a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull vfs fixes from Al Viro.

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  do_last(): ELOOP failure exit should be done after leaving RCU mode
  should_follow_link(): validate ->d_seq after having decided to follow
  namei: ->d_inode of a pinned dentry is stable only for positives
  do_last(): don't let a bogus return value from ->open() et.al. to confuse us
  fs: return -EOPNOTSUPP if clone is not supported
  hpfs: don't truncate the file when delete fails
parents 340b3a5b 5129fa48
...@@ -376,12 +376,11 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -376,12 +376,11 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
dnode_secno dno; dnode_secno dno;
int r; int r;
int rep = 0;
int err; int err;
hpfs_lock(dir->i_sb); hpfs_lock(dir->i_sb);
hpfs_adjust_length(name, &len); hpfs_adjust_length(name, &len);
again:
err = -ENOENT; err = -ENOENT;
de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh); de = map_dirent(dir, hpfs_i(dir)->i_dno, name, len, &dno, &qbh);
if (!de) if (!de)
...@@ -401,33 +400,9 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -401,33 +400,9 @@ static int hpfs_unlink(struct inode *dir, struct dentry *dentry)
hpfs_error(dir->i_sb, "there was error when removing dirent"); hpfs_error(dir->i_sb, "there was error when removing dirent");
err = -EFSERROR; err = -EFSERROR;
break; break;
case 2: /* no space for deleting, try to truncate file */ case 2: /* no space for deleting */
err = -ENOSPC; err = -ENOSPC;
if (rep++) break;
break;
dentry_unhash(dentry);
if (!d_unhashed(dentry)) {
hpfs_unlock(dir->i_sb);
return -ENOSPC;
}
if (generic_permission(inode, MAY_WRITE) ||
!S_ISREG(inode->i_mode) ||
get_write_access(inode)) {
d_rehash(dentry);
} else {
struct iattr newattrs;
/*pr_info("truncating file before delete.\n");*/
newattrs.ia_size = 0;
newattrs.ia_valid = ATTR_SIZE | ATTR_CTIME;
err = notify_change(dentry, &newattrs, NULL);
put_write_access(inode);
if (!err)
goto again;
}
hpfs_unlock(dir->i_sb);
return -ENOSPC;
default: default:
drop_nlink(inode); drop_nlink(inode);
err = 0; err = 0;
......
...@@ -1712,6 +1712,11 @@ static inline int should_follow_link(struct nameidata *nd, struct path *link, ...@@ -1712,6 +1712,11 @@ static inline int should_follow_link(struct nameidata *nd, struct path *link,
return 0; return 0;
if (!follow) if (!follow)
return 0; return 0;
/* make sure that d_is_symlink above matches inode */
if (nd->flags & LOOKUP_RCU) {
if (read_seqcount_retry(&link->dentry->d_seq, seq))
return -ECHILD;
}
return pick_link(nd, link, inode, seq); return pick_link(nd, link, inode, seq);
} }
...@@ -1743,11 +1748,11 @@ static int walk_component(struct nameidata *nd, int flags) ...@@ -1743,11 +1748,11 @@ static int walk_component(struct nameidata *nd, int flags)
if (err < 0) if (err < 0)
return err; return err;
inode = d_backing_inode(path.dentry);
seq = 0; /* we are already out of RCU mode */ seq = 0; /* we are already out of RCU mode */
err = -ENOENT; err = -ENOENT;
if (d_is_negative(path.dentry)) if (d_is_negative(path.dentry))
goto out_path_put; goto out_path_put;
inode = d_backing_inode(path.dentry);
} }
if (flags & WALK_PUT) if (flags & WALK_PUT)
...@@ -3192,12 +3197,12 @@ static int do_last(struct nameidata *nd, ...@@ -3192,12 +3197,12 @@ static int do_last(struct nameidata *nd,
return error; return error;
BUG_ON(nd->flags & LOOKUP_RCU); BUG_ON(nd->flags & LOOKUP_RCU);
inode = d_backing_inode(path.dentry);
seq = 0; /* out of RCU mode, so the value doesn't matter */ seq = 0; /* out of RCU mode, so the value doesn't matter */
if (unlikely(d_is_negative(path.dentry))) { if (unlikely(d_is_negative(path.dentry))) {
path_to_nameidata(&path, nd); path_to_nameidata(&path, nd);
return -ENOENT; return -ENOENT;
} }
inode = d_backing_inode(path.dentry);
finish_lookup: finish_lookup:
if (nd->depth) if (nd->depth)
put_link(nd); put_link(nd);
...@@ -3206,11 +3211,6 @@ static int do_last(struct nameidata *nd, ...@@ -3206,11 +3211,6 @@ static int do_last(struct nameidata *nd,
if (unlikely(error)) if (unlikely(error))
return error; return error;
if (unlikely(d_is_symlink(path.dentry)) && !(open_flag & O_PATH)) {
path_to_nameidata(&path, nd);
return -ELOOP;
}
if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) { if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path.mnt) {
path_to_nameidata(&path, nd); path_to_nameidata(&path, nd);
} else { } else {
...@@ -3229,6 +3229,10 @@ static int do_last(struct nameidata *nd, ...@@ -3229,6 +3229,10 @@ static int do_last(struct nameidata *nd,
return error; return error;
} }
audit_inode(nd->name, nd->path.dentry, 0); audit_inode(nd->name, nd->path.dentry, 0);
if (unlikely(d_is_symlink(nd->path.dentry)) && !(open_flag & O_PATH)) {
error = -ELOOP;
goto out;
}
error = -EISDIR; error = -EISDIR;
if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry)) if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
goto out; goto out;
...@@ -3273,6 +3277,10 @@ static int do_last(struct nameidata *nd, ...@@ -3273,6 +3277,10 @@ static int do_last(struct nameidata *nd,
goto exit_fput; goto exit_fput;
} }
out: out:
if (unlikely(error > 0)) {
WARN_ON(1);
error = -EINVAL;
}
if (got_write) if (got_write)
mnt_drop_write(nd->path.mnt); mnt_drop_write(nd->path.mnt);
path_put(&save_parent); path_put(&save_parent);
......
...@@ -1533,10 +1533,12 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in, ...@@ -1533,10 +1533,12 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
if (!(file_in->f_mode & FMODE_READ) || if (!(file_in->f_mode & FMODE_READ) ||
!(file_out->f_mode & FMODE_WRITE) || !(file_out->f_mode & FMODE_WRITE) ||
(file_out->f_flags & O_APPEND) || (file_out->f_flags & O_APPEND))
!file_in->f_op->clone_file_range)
return -EBADF; return -EBADF;
if (!file_in->f_op->clone_file_range)
return -EOPNOTSUPP;
ret = clone_verify_area(file_in, pos_in, len, false); ret = clone_verify_area(file_in, pos_in, len, false);
if (ret) if (ret)
return ret; return ret;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment