ioctl.c 3.91 KB
Newer Older
J. R. Okajima's avatar
J. R. Okajima committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
// SPDX-License-Identifier: GPL-2.0
/*
 * Copyright (C) 2005-2019 Junjiro R. Okajima
 */

/*
 * 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)
87
88
		h_file = au_h_open(root, wbi, wbrfd.oflags, NULL,
				   /*force_wr*/0);
J. R. Okajima's avatar
J. R. Okajima committed
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112

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;
113
	struct dentry *dentry;
J. R. Okajima's avatar
J. R. Okajima committed
114
115

	switch (cmd) {
116
117
118
119
120
	case AUFS_CTL_RDU:
	case AUFS_CTL_RDU_INO:
		err = au_rdu_ioctl(file, cmd, arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
121
122
123
124
	case AUFS_CTL_WBR_FD:
		err = au_wbr_fd(&file->f_path, (void __user *)arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
125
126
127
128
	case AUFS_CTL_IBUSY:
		err = au_ibusy_ioctl(file, arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
129
130
131
132
	case AUFS_CTL_BRINFO:
		err = au_brinfo_ioctl(file, arg);
		break;

133
134
135
136
137
138
139
140
	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;

J. R. Okajima's avatar
J. R. Okajima committed
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
	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) {
156
157
158
159
	case AUFS_CTL_MVDOWN:
		err = au_mvdown(file->f_path.dentry, (void __user *)arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
	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;

J. R. Okajima's avatar
J. R. Okajima committed
180
	switch (cmd) {
181
182
183
184
185
	case AUFS_CTL_RDU:
	case AUFS_CTL_RDU_INO:
		err = au_rdu_compat_ioctl(file, cmd, arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
186
187
188
189
	case AUFS_CTL_IBUSY:
		err = au_ibusy_compat_ioctl(file, arg);
		break;

J. R. Okajima's avatar
J. R. Okajima committed
190
191
192
193
194
195
196
	case AUFS_CTL_BRINFO:
		err = au_brinfo_compat_ioctl(file, arg);
		break;

	default:
		err = aufs_ioctl_dir(file, cmd, arg);
	}
J. R. Okajima's avatar
J. R. Okajima committed
197
198
199
200
201
202
203
204
205
206
207

	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