|
|
|
@ -721,8 +721,8 @@ static int mq_attr_ok(struct ipc_namespace *ipc_ns, struct mq_attr *attr) |
|
|
|
|
/*
|
|
|
|
|
* Invoked when creating a new queue via sys_mq_open |
|
|
|
|
*/ |
|
|
|
|
static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir, |
|
|
|
|
struct dentry *dentry, int oflag, umode_t mode, |
|
|
|
|
static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir, |
|
|
|
|
struct path *path, int oflag, umode_t mode, |
|
|
|
|
struct mq_attr *attr) |
|
|
|
|
{ |
|
|
|
|
const struct cred *cred = current_cred(); |
|
|
|
@ -732,9 +732,9 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir, |
|
|
|
|
if (attr) { |
|
|
|
|
ret = mq_attr_ok(ipc_ns, attr); |
|
|
|
|
if (ret) |
|
|
|
|
goto out; |
|
|
|
|
return ERR_PTR(ret); |
|
|
|
|
/* store for use during create */ |
|
|
|
|
dentry->d_fsdata = attr; |
|
|
|
|
path->dentry->d_fsdata = attr; |
|
|
|
|
} else { |
|
|
|
|
struct mq_attr def_attr; |
|
|
|
|
|
|
|
|
@ -744,71 +744,51 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct dentry *dir, |
|
|
|
|
ipc_ns->mq_msgsize_default); |
|
|
|
|
ret = mq_attr_ok(ipc_ns, &def_attr); |
|
|
|
|
if (ret) |
|
|
|
|
goto out; |
|
|
|
|
return ERR_PTR(ret); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
mode &= ~current_umask(); |
|
|
|
|
ret = mnt_want_write(ipc_ns->mq_mnt); |
|
|
|
|
ret = mnt_want_write(path->mnt); |
|
|
|
|
if (ret) |
|
|
|
|
goto out; |
|
|
|
|
ret = vfs_create(dir->d_inode, dentry, mode, true); |
|
|
|
|
dentry->d_fsdata = NULL; |
|
|
|
|
if (ret) |
|
|
|
|
goto out_drop_write; |
|
|
|
|
|
|
|
|
|
result = dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred); |
|
|
|
|
return ERR_PTR(ret); |
|
|
|
|
ret = vfs_create(dir, path->dentry, mode, true); |
|
|
|
|
path->dentry->d_fsdata = NULL; |
|
|
|
|
if (!ret) |
|
|
|
|
result = dentry_open(path, oflag, cred); |
|
|
|
|
else |
|
|
|
|
result = ERR_PTR(ret); |
|
|
|
|
/*
|
|
|
|
|
* dentry_open() took a persistent mnt_want_write(), |
|
|
|
|
* so we can now drop this one. |
|
|
|
|
*/ |
|
|
|
|
mnt_drop_write(ipc_ns->mq_mnt); |
|
|
|
|
mnt_drop_write(path->mnt); |
|
|
|
|
return result; |
|
|
|
|
|
|
|
|
|
out_drop_write: |
|
|
|
|
mnt_drop_write(ipc_ns->mq_mnt); |
|
|
|
|
out: |
|
|
|
|
dput(dentry); |
|
|
|
|
mntput(ipc_ns->mq_mnt); |
|
|
|
|
return ERR_PTR(ret); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Opens existing queue */ |
|
|
|
|
static struct file *do_open(struct ipc_namespace *ipc_ns, |
|
|
|
|
struct dentry *dentry, int oflag) |
|
|
|
|
static struct file *do_open(struct path *path, int oflag) |
|
|
|
|
{ |
|
|
|
|
int ret; |
|
|
|
|
const struct cred *cred = current_cred(); |
|
|
|
|
|
|
|
|
|
static const int oflag2acc[O_ACCMODE] = { MAY_READ, MAY_WRITE, |
|
|
|
|
MAY_READ | MAY_WRITE }; |
|
|
|
|
|
|
|
|
|
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) { |
|
|
|
|
ret = -EINVAL; |
|
|
|
|
goto err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (inode_permission(dentry->d_inode, oflag2acc[oflag & O_ACCMODE])) { |
|
|
|
|
ret = -EACCES; |
|
|
|
|
goto err; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return dentry_open(dentry, ipc_ns->mq_mnt, oflag, cred); |
|
|
|
|
|
|
|
|
|
err: |
|
|
|
|
dput(dentry); |
|
|
|
|
mntput(ipc_ns->mq_mnt); |
|
|
|
|
return ERR_PTR(ret); |
|
|
|
|
int acc; |
|
|
|
|
if ((oflag & O_ACCMODE) == (O_RDWR | O_WRONLY)) |
|
|
|
|
return ERR_PTR(-EINVAL); |
|
|
|
|
acc = oflag2acc[oflag & O_ACCMODE]; |
|
|
|
|
if (inode_permission(path->dentry->d_inode, acc)) |
|
|
|
|
return ERR_PTR(-EACCES); |
|
|
|
|
return dentry_open(path, oflag, current_cred()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, |
|
|
|
|
struct mq_attr __user *, u_attr) |
|
|
|
|
{ |
|
|
|
|
struct dentry *dentry; |
|
|
|
|
struct path path; |
|
|
|
|
struct file *filp; |
|
|
|
|
char *name; |
|
|
|
|
struct mq_attr attr; |
|
|
|
|
int fd, error; |
|
|
|
|
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns; |
|
|
|
|
struct dentry *root = ipc_ns->mq_mnt->mnt_root; |
|
|
|
|
|
|
|
|
|
if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr))) |
|
|
|
|
return -EFAULT; |
|
|
|
@ -822,52 +802,49 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode, |
|
|
|
|
if (fd < 0) |
|
|
|
|
goto out_putname; |
|
|
|
|
|
|
|
|
|
mutex_lock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); |
|
|
|
|
dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name)); |
|
|
|
|
if (IS_ERR(dentry)) { |
|
|
|
|
error = PTR_ERR(dentry); |
|
|
|
|
error = 0; |
|
|
|
|
mutex_lock(&root->d_inode->i_mutex); |
|
|
|
|
path.dentry = lookup_one_len(name, root, strlen(name)); |
|
|
|
|
if (IS_ERR(path.dentry)) { |
|
|
|
|
error = PTR_ERR(path.dentry); |
|
|
|
|
goto out_putfd; |
|
|
|
|
} |
|
|
|
|
mntget(ipc_ns->mq_mnt); |
|
|
|
|
path.mnt = mntget(ipc_ns->mq_mnt); |
|
|
|
|
|
|
|
|
|
if (oflag & O_CREAT) { |
|
|
|
|
if (dentry->d_inode) { /* entry already exists */ |
|
|
|
|
audit_inode(name, dentry); |
|
|
|
|
if (path.dentry->d_inode) { /* entry already exists */ |
|
|
|
|
audit_inode(name, path.dentry); |
|
|
|
|
if (oflag & O_EXCL) { |
|
|
|
|
error = -EEXIST; |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
filp = do_open(ipc_ns, dentry, oflag); |
|
|
|
|
filp = do_open(&path, oflag); |
|
|
|
|
} else { |
|
|
|
|
filp = do_create(ipc_ns, ipc_ns->mq_mnt->mnt_root, |
|
|
|
|
dentry, oflag, mode, |
|
|
|
|
filp = do_create(ipc_ns, root->d_inode, |
|
|
|
|
&path, oflag, mode, |
|
|
|
|
u_attr ? &attr : NULL); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if (!dentry->d_inode) { |
|
|
|
|
if (!path.dentry->d_inode) { |
|
|
|
|
error = -ENOENT; |
|
|
|
|
goto out; |
|
|
|
|
} |
|
|
|
|
audit_inode(name, dentry); |
|
|
|
|
filp = do_open(ipc_ns, dentry, oflag); |
|
|
|
|
audit_inode(name, path.dentry); |
|
|
|
|
filp = do_open(&path, oflag); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (IS_ERR(filp)) { |
|
|
|
|
if (!IS_ERR(filp)) |
|
|
|
|
fd_install(fd, filp); |
|
|
|
|
else |
|
|
|
|
error = PTR_ERR(filp); |
|
|
|
|
goto out_putfd; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fd_install(fd, filp); |
|
|
|
|
goto out_upsem; |
|
|
|
|
|
|
|
|
|
out: |
|
|
|
|
dput(dentry); |
|
|
|
|
mntput(ipc_ns->mq_mnt); |
|
|
|
|
path_put(&path); |
|
|
|
|
out_putfd: |
|
|
|
|
put_unused_fd(fd); |
|
|
|
|
fd = error; |
|
|
|
|
out_upsem: |
|
|
|
|
mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex); |
|
|
|
|
if (error) { |
|
|
|
|
put_unused_fd(fd); |
|
|
|
|
fd = error; |
|
|
|
|
} |
|
|
|
|
mutex_unlock(&root->d_inode->i_mutex); |
|
|
|
|
out_putname: |
|
|
|
|
putname(name); |
|
|
|
|
return fd; |
|
|
|
|