mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-23 23:50:51 +07:00
5fa3ea047a
Signed-off-by: AuxXxilium <info@auxxxilium.tech>
221 lines
4.5 KiB
C
221 lines
4.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (C) 2005-2019 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
|