]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blob - fs/aufs/ioctl.c
UBUNTU: SAUCE: AUFS
[mirror_ubuntu-jammy-kernel.git] / fs / aufs / ioctl.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Copyright (C) 2005-2021 Junjiro R. Okajima
4 *
5 * This program, aufs is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /*
20 * ioctl
21 * plink-management and readdir in userspace.
22 * assist the pathconf(3) wrapper library.
23 * move-down
24 * File-based Hierarchical Storage Management.
25 */
26
27 #include <linux/compat.h>
28 #include <linux/file.h>
29 #include "aufs.h"
30
31 static int au_wbr_fd(struct path *path, struct aufs_wbr_fd __user *arg)
32 {
33 int err, fd;
34 aufs_bindex_t wbi, bindex, bbot;
35 struct file *h_file;
36 struct super_block *sb;
37 struct dentry *root;
38 struct au_branch *br;
39 struct aufs_wbr_fd wbrfd = {
40 .oflags = au_dir_roflags,
41 .brid = -1
42 };
43 const int valid = O_RDONLY | O_NONBLOCK | O_LARGEFILE | O_DIRECTORY
44 | O_NOATIME | O_CLOEXEC;
45
46 AuDebugOn(wbrfd.oflags & ~valid);
47
48 if (arg) {
49 err = copy_from_user(&wbrfd, arg, sizeof(wbrfd));
50 if (unlikely(err)) {
51 err = -EFAULT;
52 goto out;
53 }
54
55 err = -EINVAL;
56 AuDbg("wbrfd{0%o, %d}\n", wbrfd.oflags, wbrfd.brid);
57 wbrfd.oflags |= au_dir_roflags;
58 AuDbg("0%o\n", wbrfd.oflags);
59 if (unlikely(wbrfd.oflags & ~valid))
60 goto out;
61 }
62
63 fd = get_unused_fd_flags(0);
64 err = fd;
65 if (unlikely(fd < 0))
66 goto out;
67
68 h_file = ERR_PTR(-EINVAL);
69 wbi = 0;
70 br = NULL;
71 sb = path->dentry->d_sb;
72 root = sb->s_root;
73 aufs_read_lock(root, AuLock_IR);
74 bbot = au_sbbot(sb);
75 if (wbrfd.brid >= 0) {
76 wbi = au_br_index(sb, wbrfd.brid);
77 if (unlikely(wbi < 0 || wbi > bbot))
78 goto out_unlock;
79 }
80
81 h_file = ERR_PTR(-ENOENT);
82 br = au_sbr(sb, wbi);
83 if (!au_br_writable(br->br_perm)) {
84 if (arg)
85 goto out_unlock;
86
87 bindex = wbi + 1;
88 wbi = -1;
89 for (; bindex <= bbot; bindex++) {
90 br = au_sbr(sb, bindex);
91 if (au_br_writable(br->br_perm)) {
92 wbi = bindex;
93 br = au_sbr(sb, wbi);
94 break;
95 }
96 }
97 }
98 AuDbg("wbi %d\n", wbi);
99 if (wbi >= 0)
100 h_file = au_h_open(root, wbi, wbrfd.oflags, NULL,
101 /*force_wr*/0);
102
103 out_unlock:
104 aufs_read_unlock(root, AuLock_IR);
105 err = PTR_ERR(h_file);
106 if (IS_ERR(h_file))
107 goto out_fd;
108
109 au_lcnt_dec(&br->br_nfiles); /* cf. au_h_open() */
110 fd_install(fd, h_file);
111 err = fd;
112 goto out; /* success */
113
114 out_fd:
115 put_unused_fd(fd);
116 out:
117 AuTraceErr(err);
118 return err;
119 }
120
121 /* ---------------------------------------------------------------------- */
122
123 long aufs_ioctl_dir(struct file *file, unsigned int cmd, unsigned long arg)
124 {
125 long err;
126 struct dentry *dentry;
127
128 switch (cmd) {
129 case AUFS_CTL_RDU:
130 case AUFS_CTL_RDU_INO:
131 err = au_rdu_ioctl(file, cmd, arg);
132 break;
133
134 case AUFS_CTL_WBR_FD:
135 err = au_wbr_fd(&file->f_path, (void __user *)arg);
136 break;
137
138 case AUFS_CTL_IBUSY:
139 err = au_ibusy_ioctl(file, arg);
140 break;
141
142 case AUFS_CTL_BRINFO:
143 err = au_brinfo_ioctl(file, arg);
144 break;
145
146 case AUFS_CTL_FHSM_FD:
147 dentry = file->f_path.dentry;
148 if (IS_ROOT(dentry))
149 err = au_fhsm_fd(dentry->d_sb, arg);
150 else
151 err = -ENOTTY;
152 break;
153
154 default:
155 /* do not call the lower */
156 AuDbg("0x%x\n", cmd);
157 err = -ENOTTY;
158 }
159
160 AuTraceErr(err);
161 return err;
162 }
163
164 long aufs_ioctl_nondir(struct file *file, unsigned int cmd, unsigned long arg)
165 {
166 long err;
167
168 switch (cmd) {
169 case AUFS_CTL_MVDOWN:
170 err = au_mvdown(file->f_path.dentry, (void __user *)arg);
171 break;
172
173 case AUFS_CTL_WBR_FD:
174 err = au_wbr_fd(&file->f_path, (void __user *)arg);
175 break;
176
177 default:
178 /* do not call the lower */
179 AuDbg("0x%x\n", cmd);
180 err = -ENOTTY;
181 }
182
183 AuTraceErr(err);
184 return err;
185 }
186
187 #ifdef CONFIG_COMPAT
188 long aufs_compat_ioctl_dir(struct file *file, unsigned int cmd,
189 unsigned long arg)
190 {
191 long err;
192
193 switch (cmd) {
194 case AUFS_CTL_RDU:
195 case AUFS_CTL_RDU_INO:
196 err = au_rdu_compat_ioctl(file, cmd, arg);
197 break;
198
199 case AUFS_CTL_IBUSY:
200 err = au_ibusy_compat_ioctl(file, arg);
201 break;
202
203 case AUFS_CTL_BRINFO:
204 err = au_brinfo_compat_ioctl(file, arg);
205 break;
206
207 default:
208 err = aufs_ioctl_dir(file, cmd, arg);
209 }
210
211 AuTraceErr(err);
212 return err;
213 }
214
215 long aufs_compat_ioctl_nondir(struct file *file, unsigned int cmd,
216 unsigned long arg)
217 {
218 return aufs_ioctl_nondir(file, cmd, (unsigned long)compat_ptr(arg));
219 }
220 #endif