mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
220 lines
4.5 KiB
220 lines
4.5 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2005-2020 Junjiro R. Okajima |
|
* |
|
* This program, aufs is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
|
|
/* |
|
* ioctl |
|
* plink-management and readdir in userspace. |
|
* assist the pathconf(3) wrapper library. |
|
* move-down |
|
* File-based Hierarchical Storage Management. |
|
*/ |
|
|
|
#include <linux/compat.h> |
|
#include <linux/file.h> |
|
#include "aufs.h" |
|
|
|
static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg) |
|
{ |
|
int err, fd; |
|
aufs_bindex_t wbi, bindex, bbot; |
|
struct file *h_file; |
|
struct super_block *sb; |
|
struct dentry *root; |
|
struct au_branch *br; |
|
struct aufs_wbr_fd wbrfd = { |
|
.oflags = au_dir_roflags, |
|
.brid = -1 |
|
}; |
|
const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY |
|
| O_NOATIME | O_CLOEXEC; |
|
|
|
AuDebugOn(wbrfd.oflags & ~valid); |
|
|
|
if (arg) { |
|
err = copy_from_user(&wbrfd, arg, sizeof(wbrfd)); |
|
if (unlikely(err)) { |
|
err = -EFAULT; |
|
goto out; |
|
} |
|
|
|
err = -EINVAL; |
|
AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid); |
|
wbrfd.oflags |= au_dir_roflags; |
|
AuDbg("0%o\n", wbrfd.oflags); |
|
if (unlikely(wbrfd.oflags & ~valid)) |
|
goto out; |
|
} |
|
|
|
fd = get_unused_fd_flags(0); |
|
err = fd; |
|
if (unlikely(fd < 0)) |
|
goto out; |
|
|
|
h_file = ERR_PTR(-EINVAL); |
|
wbi = 0; |
|
br = NULL; |
|
sb = path->dentry->d_sb; |
|
root = sb->s_root; |
|
aufs_read_lock(root, AuLock_IR); |
|
bbot = au_sbbot(sb); |
|
if (wbrfd.brid >= 0) { |
|
wbi = au_br_index(sb, wbrfd.brid); |
|
if (unlikely(wbi < 0 || wbi > bbot)) |
|
goto out_unlock; |
|
} |
|
|
|
h_file = ERR_PTR(-ENOENT); |
|
br = au_sbr(sb, wbi); |
|
if (!au_br_writable(br->br_perm)) { |
|
if (arg) |
|
goto out_unlock; |
|
|
|
bindex = wbi + 1; |
|
wbi = -1; |
|
for (; bindex <= bbot; bindex++) { |
|
br = au_sbr(sb, bindex); |
|
if (au_br_writable(br->br_perm)) { |
|
wbi = bindex; |
|
br = au_sbr(sb, wbi); |
|
break; |
|
} |
|
} |
|
} |
|
AuDbg("wbi %d\n", wbi); |
|
if (wbi >= 0) |
|
h_file = au_h_open(root, wbi, wbrfd.oflags, NULL, |
|
/*force_wr*/0); |
|
|
|
out_unlock: |
|
aufs_read_unlock(root, AuLock_IR); |
|
err = PTR_ERR(h_file); |
|
if (IS_ERR(h_file)) |
|
goto out_fd; |
|
|
|
au_lcnt_dec(&br->br_nfiles); /* cf. au_h_open() */ |
|
fd_install(fd, h_file); |
|
err = fd; |
|
goto out; /* success */ |
|
|
|
out_fd: |
|
put_unused_fd(fd); |
|
out: |
|
AuTraceErr(err); |
|
return err; |
|
} |
|
|
|
/* ---------------------------------------------------------------------- */ |
|
|
|
long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg) |
|
{ |
|
long err; |
|
struct dentry *dentry; |
|
|
|
switch (cmd) { |
|
case AUFS_CTL_RDU: |
|
case AUFS_CTL_RDU_INO: |
|
err = au_rdu_ioctl(file, cmd, arg); |
|
break; |
|
|
|
case AUFS_CTL_WBR_FD: |
|
err = au_wbr_fd(&file->f_path, (void __user *)arg); |
|
break; |
|
|
|
case AUFS_CTL_IBUSY: |
|
err = au_ibusy_ioctl(file, arg); |
|
break; |
|
|
|
case AUFS_CTL_BRINFO: |
|
err = au_brinfo_ioctl(file, arg); |
|
break; |
|
|
|
case AUFS_CTL_FHSM_FD: |
|
dentry = file->f_path.dentry; |
|
if (IS_ROOT(dentry)) |
|
err = au_fhsm_fd(dentry->d_sb, arg); |
|
else |
|
err = -ENOTTY; |
|
break; |
|
|
|
default: |
|
/* do not call the lower */ |
|
AuDbg("0x%x\n", cmd); |
|
err = -ENOTTY; |
|
} |
|
|
|
AuTraceErr(err); |
|
return err; |
|
} |
|
|
|
long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg) |
|
{ |
|
long err; |
|
|
|
switch (cmd) { |
|
case AUFS_CTL_MVDOWN: |
|
err = au_mvdown(file->f_path.dentry, (void __user *)arg); |
|
break; |
|
|
|
case AUFS_CTL_WBR_FD: |
|
err = au_wbr_fd(&file->f_path, (void __user *)arg); |
|
break; |
|
|
|
default: |
|
/* do not call the lower */ |
|
AuDbg("0x%x\n", cmd); |
|
err = -ENOTTY; |
|
} |
|
|
|
AuTraceErr(err); |
|
return err; |
|
} |
|
|
|
#ifdef CONFIG_COMPAT |
|
long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd, |
|
unsigned long arg) |
|
{ |
|
long err; |
|
|
|
switch (cmd) { |
|
case AUFS_CTL_RDU: |
|
case AUFS_CTL_RDU_INO: |
|
err = au_rdu_compat_ioctl(file, cmd, arg); |
|
break; |
|
|
|
case AUFS_CTL_IBUSY: |
|
err = au_ibusy_compat_ioctl(file, arg); |
|
break; |
|
|
|
case AUFS_CTL_BRINFO: |
|
err = au_brinfo_compat_ioctl(file, arg); |
|
break; |
|
|
|
default: |
|
err = aufs_ioctl_dir(file, cmd, arg); |
|
} |
|
|
|
AuTraceErr(err); |
|
return err; |
|
} |
|
|
|
long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd, |
|
unsigned long arg) |
|
{ |
|
return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg)); |
|
} |
|
#endif
|
|
|