Commit | Line | Data |
---|---|---|
09c434b8 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
1da177e4 | 2 | /* |
1da177e4 LT |
3 | * Syscall interface to knfsd. |
4 | * | |
5 | * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de> | |
6 | */ | |
7 | ||
5a0e3ad6 | 8 | #include <linux/slab.h> |
3f8206d4 | 9 | #include <linux/namei.h> |
b41b66d6 | 10 | #include <linux/ctype.h> |
96a374a3 | 11 | #include <linux/fs_context.h> |
1da177e4 | 12 | |
80212d59 | 13 | #include <linux/sunrpc/svcsock.h> |
4373ea84 | 14 | #include <linux/lockd/lockd.h> |
5976687a | 15 | #include <linux/sunrpc/addr.h> |
b0b0c0a2 | 16 | #include <linux/sunrpc/gss_api.h> |
813fd320 | 17 | #include <linux/sunrpc/rpc_pipe_fs.h> |
143cb494 | 18 | #include <linux/module.h> |
e8a79fb1 | 19 | #include <linux/fsnotify.h> |
1da177e4 | 20 | |
2ca72e17 | 21 | #include "idmap.h" |
9a74af21 BH |
22 | #include "nfsd.h" |
23 | #include "cache.h" | |
f3c7521f | 24 | #include "state.h" |
7ea34ac1 | 25 | #include "netns.h" |
9cf514cc | 26 | #include "pnfs.h" |
2e6c6e4c | 27 | #include "filecache.h" |
39d432fc | 28 | #include "trace.h" |
13727f85 | 29 | #include "netlink.h" |
9a74af21 | 30 | |
1da177e4 | 31 | /* |
b0b0c0a2 | 32 | * We have a single directory with several nodes in it. |
1da177e4 LT |
33 | */ |
34 | enum { | |
35 | NFSD_Root = 1, | |
1da177e4 | 36 | NFSD_List, |
20ad856e | 37 | NFSD_Export_Stats, |
e8e8753f | 38 | NFSD_Export_features, |
1da177e4 | 39 | NFSD_Fh, |
4373ea84 | 40 | NFSD_FO_UnlockIP, |
17efa372 | 41 | NFSD_FO_UnlockFS, |
1da177e4 | 42 | NFSD_Threads, |
eed2965a | 43 | NFSD_Pool_Threads, |
03cf6c9f | 44 | NFSD_Pool_Stats, |
a2f999a3 | 45 | NFSD_Reply_Cache_Stats, |
70c3b76c | 46 | NFSD_Versions, |
80212d59 | 47 | NFSD_Ports, |
596bbe53 | 48 | NFSD_MaxBlkSize, |
5b8db00b | 49 | NFSD_MaxConnections, |
2e6c6e4c | 50 | NFSD_Filecache, |
70c3b76c | 51 | #ifdef CONFIG_NFSD_V4 |
1da177e4 | 52 | NFSD_Leasetime, |
efc4bb4f | 53 | NFSD_Gracetime, |
0964a3d3 | 54 | NFSD_RecoveryDir, |
7f5ef2e9 | 55 | NFSD_V4EndGrace, |
70c3b76c | 56 | #endif |
e8a79fb1 | 57 | NFSD_MaxReserved |
1da177e4 LT |
58 | }; |
59 | ||
60 | /* | |
61 | * write() for these nodes. | |
62 | */ | |
1da177e4 | 63 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size); |
b046ccdc CL |
64 | static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size); |
65 | static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size); | |
1da177e4 | 66 | static ssize_t write_threads(struct file *file, char *buf, size_t size); |
eed2965a | 67 | static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); |
70c3b76c | 68 | static ssize_t write_versions(struct file *file, char *buf, size_t size); |
80212d59 | 69 | static ssize_t write_ports(struct file *file, char *buf, size_t size); |
596bbe53 | 70 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); |
5b8db00b | 71 | static ssize_t write_maxconn(struct file *file, char *buf, size_t size); |
70c3b76c | 72 | #ifdef CONFIG_NFSD_V4 |
1da177e4 | 73 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size); |
efc4bb4f | 74 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size); |
74fd4873 | 75 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
0964a3d3 | 76 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); |
74fd4873 | 77 | #endif |
7f5ef2e9 | 78 | static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size); |
70c3b76c | 79 | #endif |
1da177e4 | 80 | |
c2cdc2ab | 81 | static ssize_t (*const write_op[])(struct file *, char *, size_t) = { |
1da177e4 | 82 | [NFSD_Fh] = write_filehandle, |
b046ccdc CL |
83 | [NFSD_FO_UnlockIP] = write_unlock_ip, |
84 | [NFSD_FO_UnlockFS] = write_unlock_fs, | |
1da177e4 | 85 | [NFSD_Threads] = write_threads, |
eed2965a | 86 | [NFSD_Pool_Threads] = write_pool_threads, |
70c3b76c | 87 | [NFSD_Versions] = write_versions, |
80212d59 | 88 | [NFSD_Ports] = write_ports, |
596bbe53 | 89 | [NFSD_MaxBlkSize] = write_maxblksize, |
5b8db00b | 90 | [NFSD_MaxConnections] = write_maxconn, |
70c3b76c | 91 | #ifdef CONFIG_NFSD_V4 |
1da177e4 | 92 | [NFSD_Leasetime] = write_leasetime, |
efc4bb4f | 93 | [NFSD_Gracetime] = write_gracetime, |
74fd4873 | 94 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
0964a3d3 | 95 | [NFSD_RecoveryDir] = write_recoverydir, |
74fd4873 | 96 | #endif |
7f5ef2e9 | 97 | [NFSD_V4EndGrace] = write_v4_end_grace, |
70c3b76c | 98 | #endif |
1da177e4 LT |
99 | }; |
100 | ||
101 | static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | |
102 | { | |
496ad9aa | 103 | ino_t ino = file_inode(file)->i_ino; |
1da177e4 LT |
104 | char *data; |
105 | ssize_t rv; | |
106 | ||
e8c96f8c | 107 | if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) |
1da177e4 LT |
108 | return -EINVAL; |
109 | ||
110 | data = simple_transaction_get(file, buf, size); | |
111 | if (IS_ERR(data)) | |
112 | return PTR_ERR(data); | |
113 | ||
3434d7aa CL |
114 | rv = write_op[ino](file, data, size); |
115 | if (rv < 0) | |
116 | return rv; | |
117 | ||
118 | simple_transaction_set(file, rv); | |
119 | return size; | |
1da177e4 LT |
120 | } |
121 | ||
7390022d N |
122 | static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) |
123 | { | |
124 | if (! file->private_data) { | |
125 | /* An attempt to read a transaction file without writing | |
126 | * causes a 0-byte write so that the file can return | |
127 | * state information | |
128 | */ | |
129 | ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); | |
130 | if (rv < 0) | |
131 | return rv; | |
132 | } | |
133 | return simple_transaction_read(file, buf, size, pos); | |
134 | } | |
135 | ||
4b6f5d20 | 136 | static const struct file_operations transaction_ops = { |
1da177e4 | 137 | .write = nfsctl_transaction_write, |
7390022d | 138 | .read = nfsctl_transaction_read, |
1da177e4 | 139 | .release = simple_transaction_release, |
6038f373 | 140 | .llseek = default_llseek, |
1da177e4 LT |
141 | }; |
142 | ||
96d851c4 | 143 | static int exports_net_open(struct net *net, struct file *file) |
1da177e4 | 144 | { |
f2c7ea10 SK |
145 | int err; |
146 | struct seq_file *seq; | |
96d851c4 | 147 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
f2c7ea10 SK |
148 | |
149 | err = seq_open(file, &nfs_exports_op); | |
150 | if (err) | |
151 | return err; | |
152 | ||
153 | seq = file->private_data; | |
e5f06f72 | 154 | seq->private = nn->svc_export_cache; |
f2c7ea10 | 155 | return 0; |
1da177e4 LT |
156 | } |
157 | ||
96d851c4 SK |
158 | static int exports_nfsd_open(struct inode *inode, struct file *file) |
159 | { | |
160 | return exports_net_open(inode->i_sb->s_fs_info, file); | |
161 | } | |
162 | ||
163 | static const struct file_operations exports_nfsd_operations = { | |
164 | .open = exports_nfsd_open, | |
1da177e4 LT |
165 | .read = seq_read, |
166 | .llseek = seq_lseek, | |
167 | .release = seq_release, | |
168 | }; | |
169 | ||
e8e8753f BF |
170 | static int export_features_show(struct seq_file *m, void *v) |
171 | { | |
172 | seq_printf(m, "0x%x 0x%x\n", NFSEXP_ALLFLAGS, NFSEXP_SECINFO_FLAGS); | |
173 | return 0; | |
174 | } | |
175 | ||
9beeaab8 | 176 | DEFINE_SHOW_ATTRIBUTE(export_features); |
e8e8753f | 177 | |
828c0950 | 178 | static const struct file_operations pool_stats_operations = { |
03cf6c9f GB |
179 | .open = nfsd_pool_stats_open, |
180 | .read = seq_read, | |
181 | .llseek = seq_lseek, | |
7b207ccd | 182 | .release = seq_release, |
03cf6c9f GB |
183 | }; |
184 | ||
64776611 | 185 | DEFINE_SHOW_ATTRIBUTE(nfsd_reply_cache_stats); |
a2f999a3 | 186 | |
1342f9dd | 187 | DEFINE_SHOW_ATTRIBUTE(nfsd_file_cache_stats); |
2e6c6e4c | 188 | |
1da177e4 LT |
189 | /*----------------------------------------------------------------------------*/ |
190 | /* | |
191 | * payload - write methods | |
1da177e4 LT |
192 | */ |
193 | ||
244c7d44 AV |
194 | static inline struct net *netns(struct file *file) |
195 | { | |
196 | return file_inode(file)->i_sb->s_fs_info; | |
197 | } | |
1da177e4 | 198 | |
f2453978 | 199 | /* |
262a0982 CL |
200 | * write_unlock_ip - Release all locks used by a client |
201 | * | |
202 | * Experimental. | |
203 | * | |
204 | * Input: | |
205 | * buf: '\n'-terminated C string containing a | |
4116092b | 206 | * presentation format IP address |
262a0982 CL |
207 | * size: length of C string in @buf |
208 | * Output: | |
209 | * On success: returns zero if all specified locks were released; | |
210 | * returns one if one or more locks were not released | |
211 | * On error: return code is negative errno value | |
262a0982 | 212 | */ |
b046ccdc | 213 | static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) |
4373ea84 | 214 | { |
4116092b CL |
215 | struct sockaddr_storage address; |
216 | struct sockaddr *sap = (struct sockaddr *)&address; | |
217 | size_t salen = sizeof(address); | |
367c8c7b | 218 | char *fo_path; |
244c7d44 | 219 | struct net *net = netns(file); |
4373ea84 WC |
220 | |
221 | /* sanity check */ | |
222 | if (size == 0) | |
223 | return -EINVAL; | |
224 | ||
225 | if (buf[size-1] != '\n') | |
226 | return -EINVAL; | |
227 | ||
228 | fo_path = buf; | |
229 | if (qword_get(&buf, fo_path, size) < 0) | |
230 | return -EINVAL; | |
231 | ||
11f77942 | 232 | if (rpc_pton(net, fo_path, size, sap, salen) == 0) |
4373ea84 | 233 | return -EINVAL; |
4373ea84 | 234 | |
39d432fc | 235 | trace_nfsd_ctl_unlock_ip(net, buf); |
4116092b | 236 | return nlmsvc_unlock_all_by_ip(sap); |
4373ea84 WC |
237 | } |
238 | ||
f2453978 | 239 | /* |
262a0982 CL |
240 | * write_unlock_fs - Release all locks on a local file system |
241 | * | |
242 | * Experimental. | |
243 | * | |
244 | * Input: | |
245 | * buf: '\n'-terminated C string containing the | |
246 | * absolute pathname of a local file system | |
247 | * size: length of C string in @buf | |
248 | * Output: | |
249 | * On success: returns zero if all specified locks were released; | |
250 | * returns one if one or more locks were not released | |
251 | * On error: return code is negative errno value | |
252 | */ | |
b046ccdc | 253 | static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) |
17efa372 | 254 | { |
a63bb996 | 255 | struct path path; |
17efa372 WC |
256 | char *fo_path; |
257 | int error; | |
258 | ||
259 | /* sanity check */ | |
260 | if (size == 0) | |
261 | return -EINVAL; | |
262 | ||
263 | if (buf[size-1] != '\n') | |
264 | return -EINVAL; | |
265 | ||
266 | fo_path = buf; | |
267 | if (qword_get(&buf, fo_path, size) < 0) | |
268 | return -EINVAL; | |
39d432fc | 269 | trace_nfsd_ctl_unlock_fs(netns(file), fo_path); |
a63bb996 | 270 | error = kern_path(fo_path, 0, &path); |
17efa372 WC |
271 | if (error) |
272 | return error; | |
273 | ||
262a0982 CL |
274 | /* |
275 | * XXX: Needs better sanity checking. Otherwise we could end up | |
276 | * releasing locks on the wrong file system. | |
277 | * | |
278 | * For example: | |
279 | * 1. Does the path refer to a directory? | |
280 | * 2. Is that directory a mount point, or | |
281 | * 3. Is that directory the root of an exported file system? | |
282 | */ | |
d8c9584e | 283 | error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); |
17efa372 | 284 | |
a63bb996 | 285 | path_put(&path); |
17efa372 WC |
286 | return error; |
287 | } | |
288 | ||
f2453978 | 289 | /* |
262a0982 CL |
290 | * write_filehandle - Get a variable-length NFS file handle by path |
291 | * | |
292 | * On input, the buffer contains a '\n'-terminated C string comprised of | |
293 | * three alphanumeric words separated by whitespace. The string may | |
294 | * contain escape sequences. | |
295 | * | |
296 | * Input: | |
297 | * buf: | |
298 | * domain: client domain name | |
299 | * path: export pathname | |
300 | * maxsize: numeric maximum size of | |
301 | * @buf | |
302 | * size: length of C string in @buf | |
303 | * Output: | |
304 | * On success: passed-in buffer filled with '\n'-terminated C | |
305 | * string containing a ASCII hex text version | |
306 | * of the NFS file handle; | |
307 | * return code is the size in bytes of the string | |
308 | * On error: return code is negative errno value | |
309 | */ | |
1da177e4 LT |
310 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size) |
311 | { | |
1da177e4 | 312 | char *dname, *path; |
3f649ab7 | 313 | int maxsize; |
1da177e4 LT |
314 | char *mesg = buf; |
315 | int len; | |
316 | struct auth_domain *dom; | |
317 | struct knfsd_fh fh; | |
318 | ||
87d26ea7 BF |
319 | if (size == 0) |
320 | return -EINVAL; | |
321 | ||
1da177e4 LT |
322 | if (buf[size-1] != '\n') |
323 | return -EINVAL; | |
324 | buf[size-1] = 0; | |
325 | ||
326 | dname = mesg; | |
327 | len = qword_get(&mesg, dname, size); | |
54224f04 CL |
328 | if (len <= 0) |
329 | return -EINVAL; | |
442a6290 | 330 | |
1da177e4 LT |
331 | path = dname+len+1; |
332 | len = qword_get(&mesg, path, size); | |
54224f04 CL |
333 | if (len <= 0) |
334 | return -EINVAL; | |
1da177e4 LT |
335 | |
336 | len = get_int(&mesg, &maxsize); | |
337 | if (len) | |
338 | return len; | |
339 | ||
340 | if (maxsize < NFS_FHSIZE) | |
341 | return -EINVAL; | |
3c7aa15d | 342 | maxsize = min(maxsize, NFS3_FHSIZE); |
1da177e4 | 343 | |
442a6290 | 344 | if (qword_get(&mesg, mesg, size) > 0) |
1da177e4 LT |
345 | return -EINVAL; |
346 | ||
39d432fc CL |
347 | trace_nfsd_ctl_filehandle(netns(file), dname, path, maxsize); |
348 | ||
1da177e4 LT |
349 | /* we have all the words, they are in buf.. */ |
350 | dom = unix_domain_find(dname); | |
351 | if (!dom) | |
352 | return -ENOMEM; | |
353 | ||
442a6290 | 354 | len = exp_rootfh(netns(file), dom, path, &fh, maxsize); |
1da177e4 LT |
355 | auth_domain_put(dom); |
356 | if (len) | |
357 | return len; | |
d8b26071 | 358 | |
54224f04 CL |
359 | mesg = buf; |
360 | len = SIMPLE_TRANSACTION_LIMIT; | |
d8b26071 | 361 | qword_addhex(&mesg, &len, fh.fh_raw, fh.fh_size); |
1da177e4 | 362 | mesg[-1] = '\n'; |
d8b26071 | 363 | return mesg - buf; |
1da177e4 LT |
364 | } |
365 | ||
f2453978 | 366 | /* |
262a0982 CL |
367 | * write_threads - Start NFSD, or report the current number of running threads |
368 | * | |
369 | * Input: | |
370 | * buf: ignored | |
371 | * size: zero | |
372 | * Output: | |
373 | * On success: passed-in buffer filled with '\n'-terminated C | |
374 | * string numeric value representing the number of | |
375 | * running NFSD threads; | |
376 | * return code is the size in bytes of the string | |
377 | * On error: return code is zero | |
378 | * | |
379 | * OR | |
380 | * | |
381 | * Input: | |
382 | * buf: C string containing an unsigned | |
383 | * integer value representing the | |
384 | * number of NFSD threads to start | |
385 | * size: non-zero length of C string in @buf | |
386 | * Output: | |
387 | * On success: NFS service is started; | |
388 | * passed-in buffer filled with '\n'-terminated C | |
389 | * string numeric value representing the number of | |
390 | * running NFSD threads; | |
391 | * return code is the size in bytes of the string | |
392 | * On error: return code is zero or a negative errno value | |
393 | */ | |
1da177e4 LT |
394 | static ssize_t write_threads(struct file *file, char *buf, size_t size) |
395 | { | |
1da177e4 LT |
396 | char *mesg = buf; |
397 | int rv; | |
244c7d44 | 398 | struct net *net = netns(file); |
d41a9417 | 399 | |
1da177e4 LT |
400 | if (size > 0) { |
401 | int newthreads; | |
402 | rv = get_int(&mesg, &newthreads); | |
403 | if (rv) | |
404 | return rv; | |
9e074856 | 405 | if (newthreads < 0) |
1da177e4 | 406 | return -EINVAL; |
39d432fc | 407 | trace_nfsd_ctl_threads(net, newthreads); |
4df493a2 | 408 | rv = nfsd_svc(newthreads, net, file->f_cred); |
82e12fe9 | 409 | if (rv < 0) |
1da177e4 | 410 | return rv; |
82e12fe9 | 411 | } else |
9dd9845f | 412 | rv = nfsd_nrthreads(net); |
e06b6405 | 413 | |
82e12fe9 | 414 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); |
1da177e4 LT |
415 | } |
416 | ||
f2453978 | 417 | /* |
262a0982 CL |
418 | * write_pool_threads - Set or report the current number of threads per pool |
419 | * | |
420 | * Input: | |
421 | * buf: ignored | |
422 | * size: zero | |
423 | * | |
424 | * OR | |
425 | * | |
426 | * Input: | |
442a6290 CL |
427 | * buf: C string containing whitespace- |
428 | * separated unsigned integer values | |
262a0982 CL |
429 | * representing the number of NFSD |
430 | * threads to start in each pool | |
431 | * size: non-zero length of C string in @buf | |
432 | * Output: | |
433 | * On success: passed-in buffer filled with '\n'-terminated C | |
434 | * string containing integer values representing the | |
435 | * number of NFSD threads in each pool; | |
436 | * return code is the size in bytes of the string | |
437 | * On error: return code is zero or a negative errno value | |
438 | */ | |
eed2965a GB |
439 | static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) |
440 | { | |
441 | /* if size > 0, look for an array of number of threads per node | |
442 | * and apply them then write out number of threads per node as reply | |
443 | */ | |
444 | char *mesg = buf; | |
445 | int i; | |
446 | int rv; | |
447 | int len; | |
bedbdd8b | 448 | int npools; |
eed2965a | 449 | int *nthreads; |
244c7d44 | 450 | struct net *net = netns(file); |
eed2965a | 451 | |
bedbdd8b | 452 | mutex_lock(&nfsd_mutex); |
9dd9845f | 453 | npools = nfsd_nrpools(net); |
eed2965a GB |
454 | if (npools == 0) { |
455 | /* | |
456 | * NFS is shut down. The admin can start it by | |
457 | * writing to the threads file but NOT the pool_threads | |
458 | * file, sorry. Report zero threads. | |
459 | */ | |
bedbdd8b | 460 | mutex_unlock(&nfsd_mutex); |
eed2965a GB |
461 | strcpy(buf, "0\n"); |
462 | return strlen(buf); | |
463 | } | |
464 | ||
465 | nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); | |
bedbdd8b | 466 | rv = -ENOMEM; |
eed2965a | 467 | if (nthreads == NULL) |
bedbdd8b | 468 | goto out_free; |
eed2965a GB |
469 | |
470 | if (size > 0) { | |
471 | for (i = 0; i < npools; i++) { | |
472 | rv = get_int(&mesg, &nthreads[i]); | |
473 | if (rv == -ENOENT) | |
474 | break; /* fewer numbers than pools */ | |
475 | if (rv) | |
476 | goto out_free; /* syntax error */ | |
477 | rv = -EINVAL; | |
478 | if (nthreads[i] < 0) | |
479 | goto out_free; | |
39d432fc | 480 | trace_nfsd_ctl_pool_threads(net, i, nthreads[i]); |
eed2965a | 481 | } |
3938a0d5 | 482 | rv = nfsd_set_nrthreads(i, nthreads, net); |
eed2965a GB |
483 | if (rv) |
484 | goto out_free; | |
485 | } | |
486 | ||
9dd9845f | 487 | rv = nfsd_get_nrthreads(npools, nthreads, net); |
eed2965a GB |
488 | if (rv) |
489 | goto out_free; | |
490 | ||
491 | mesg = buf; | |
492 | size = SIMPLE_TRANSACTION_LIMIT; | |
493 | for (i = 0; i < npools && size > 0; i++) { | |
494 | snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); | |
495 | len = strlen(mesg); | |
496 | size -= len; | |
497 | mesg += len; | |
498 | } | |
413d63d7 | 499 | rv = mesg - buf; |
eed2965a GB |
500 | out_free: |
501 | kfree(nthreads); | |
bedbdd8b | 502 | mutex_unlock(&nfsd_mutex); |
eed2965a GB |
503 | return rv; |
504 | } | |
505 | ||
ff7d1179 | 506 | static ssize_t |
e333f3bb TM |
507 | nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining, |
508 | const char *sep, unsigned vers, int minor) | |
ff7d1179 | 509 | { |
abcb4dac | 510 | const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; |
e333f3bb | 511 | bool supported = !!nfsd_vers(nn, vers, NFSD_TEST); |
ff7d1179 | 512 | |
abcb4dac | 513 | if (vers == 4 && minor >= 0 && |
e333f3bb | 514 | !nfsd_minorversion(nn, minor, NFSD_TEST)) |
ff7d1179 | 515 | supported = false; |
abcb4dac N |
516 | if (minor == 0 && supported) |
517 | /* | |
518 | * special case for backward compatability. | |
519 | * +4.0 is never reported, it is implied by | |
520 | * +4, unless -4.0 is present. | |
521 | */ | |
522 | return 0; | |
ff7d1179 TM |
523 | return snprintf(buf, remaining, format, sep, |
524 | supported ? '+' : '-', vers, minor); | |
525 | } | |
526 | ||
3dd98a3b | 527 | static ssize_t __write_versions(struct file *file, char *buf, size_t size) |
70c3b76c | 528 | { |
70c3b76c | 529 | char *mesg = buf; |
8daf220a | 530 | char *vers, *minorp, sign; |
261758b5 | 531 | int len, num, remaining; |
70c3b76c N |
532 | ssize_t tlen = 0; |
533 | char *sep; | |
244c7d44 | 534 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
70c3b76c | 535 | |
442a6290 | 536 | if (size > 0) { |
9dd9845f | 537 | if (nn->nfsd_serv) |
6658d3a7 | 538 | /* Cannot change versions without updating |
9dd9845f | 539 | * nn->nfsd_serv->sv_xdrsize, and reallocing |
6658d3a7 N |
540 | * rq_argp and rq_resp |
541 | */ | |
70c3b76c N |
542 | return -EBUSY; |
543 | if (buf[size-1] != '\n') | |
544 | return -EINVAL; | |
545 | buf[size-1] = 0; | |
39d432fc | 546 | trace_nfsd_ctl_version(netns(file), buf); |
70c3b76c N |
547 | |
548 | vers = mesg; | |
549 | len = qword_get(&mesg, vers, size); | |
550 | if (len <= 0) return -EINVAL; | |
551 | do { | |
d3635ff0 | 552 | enum vers_op cmd; |
abcb4dac | 553 | unsigned minor; |
70c3b76c N |
554 | sign = *vers; |
555 | if (sign == '+' || sign == '-') | |
8daf220a | 556 | num = simple_strtol((vers+1), &minorp, 0); |
70c3b76c | 557 | else |
8daf220a BH |
558 | num = simple_strtol(vers, &minorp, 0); |
559 | if (*minorp == '.') { | |
ff89be87 | 560 | if (num != 4) |
8daf220a | 561 | return -EINVAL; |
e35659f1 | 562 | if (kstrtouint(minorp+1, 0, &minor) < 0) |
8daf220a | 563 | return -EINVAL; |
abcb4dac N |
564 | } |
565 | ||
d3635ff0 | 566 | cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; |
70c3b76c | 567 | switch(num) { |
2f3a4b2a | 568 | #ifdef CONFIG_NFSD_V2 |
70c3b76c | 569 | case 2: |
2f3a4b2a | 570 | #endif |
70c3b76c | 571 | case 3: |
e333f3bb | 572 | nfsd_vers(nn, num, cmd); |
70c3b76c | 573 | break; |
d3635ff0 | 574 | case 4: |
abcb4dac | 575 | if (*minorp == '.') { |
e333f3bb | 576 | if (nfsd_minorversion(nn, minor, cmd) < 0) |
abcb4dac | 577 | return -EINVAL; |
e333f3bb | 578 | } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { |
abcb4dac N |
579 | /* |
580 | * Either we have +4 and no minors are enabled, | |
581 | * or we have -4 and at least one minor is enabled. | |
582 | * In either case, propagate 'cmd' to all minors. | |
583 | */ | |
584 | minor = 0; | |
e333f3bb | 585 | while (nfsd_minorversion(nn, minor, cmd) >= 0) |
abcb4dac N |
586 | minor++; |
587 | } | |
588 | break; | |
70c3b76c | 589 | default: |
8e823baf JL |
590 | /* Ignore requests to disable non-existent versions */ |
591 | if (cmd == NFSD_SET) | |
592 | return -EINVAL; | |
70c3b76c N |
593 | } |
594 | vers += len + 1; | |
70c3b76c N |
595 | } while ((len = qword_get(&mesg, vers, size)) > 0); |
596 | /* If all get turned off, turn them back on, as | |
597 | * having no versions is BAD | |
598 | */ | |
e333f3bb | 599 | nfsd_reset_versions(nn); |
70c3b76c | 600 | } |
261758b5 | 601 | |
70c3b76c | 602 | /* Now write current state into reply buffer */ |
70c3b76c | 603 | sep = ""; |
261758b5 | 604 | remaining = SIMPLE_TRANSACTION_LIMIT; |
ff7d1179 | 605 | for (num=2 ; num <= 4 ; num++) { |
abcb4dac | 606 | int minor; |
e333f3bb | 607 | if (!nfsd_vers(nn, num, NFSD_AVAIL)) |
ff7d1179 | 608 | continue; |
abcb4dac N |
609 | |
610 | minor = -1; | |
ff7d1179 | 611 | do { |
e333f3bb | 612 | len = nfsd_print_version_support(nn, buf, remaining, |
ff7d1179 | 613 | sep, num, minor); |
818f2f57 | 614 | if (len >= remaining) |
ff7d1179 | 615 | goto out; |
261758b5 CL |
616 | remaining -= len; |
617 | buf += len; | |
618 | tlen += len; | |
ff7d1179 | 619 | minor++; |
abcb4dac N |
620 | if (len) |
621 | sep = " "; | |
ff7d1179 TM |
622 | } while (num == 4 && minor <= NFSD_SUPPORTED_MINOR_VERSION); |
623 | } | |
624 | out: | |
261758b5 | 625 | len = snprintf(buf, remaining, "\n"); |
818f2f57 | 626 | if (len >= remaining) |
261758b5 CL |
627 | return -EINVAL; |
628 | return tlen + len; | |
70c3b76c N |
629 | } |
630 | ||
f2453978 | 631 | /* |
262a0982 CL |
632 | * write_versions - Set or report the available NFS protocol versions |
633 | * | |
634 | * Input: | |
635 | * buf: ignored | |
636 | * size: zero | |
637 | * Output: | |
638 | * On success: passed-in buffer filled with '\n'-terminated C | |
639 | * string containing positive or negative integer | |
640 | * values representing the current status of each | |
641 | * protocol version; | |
642 | * return code is the size in bytes of the string | |
643 | * On error: return code is zero or a negative errno value | |
644 | * | |
645 | * OR | |
646 | * | |
647 | * Input: | |
442a6290 CL |
648 | * buf: C string containing whitespace- |
649 | * separated positive or negative | |
650 | * integer values representing NFS | |
651 | * protocol versions to enable ("+n") | |
652 | * or disable ("-n") | |
262a0982 CL |
653 | * size: non-zero length of C string in @buf |
654 | * Output: | |
655 | * On success: status of zero or more protocol versions has | |
656 | * been updated; passed-in buffer filled with | |
657 | * '\n'-terminated C string containing positive | |
658 | * or negative integer values representing the | |
659 | * current status of each protocol version; | |
660 | * return code is the size in bytes of the string | |
661 | * On error: return code is zero or a negative errno value | |
662 | */ | |
3dd98a3b JL |
663 | static ssize_t write_versions(struct file *file, char *buf, size_t size) |
664 | { | |
665 | ssize_t rv; | |
666 | ||
667 | mutex_lock(&nfsd_mutex); | |
668 | rv = __write_versions(file, buf, size); | |
669 | mutex_unlock(&nfsd_mutex); | |
670 | return rv; | |
671 | } | |
672 | ||
0a5372d8 CL |
673 | /* |
674 | * Zero-length write. Return a list of NFSD's current listener | |
675 | * transports. | |
676 | */ | |
9dd9845f | 677 | static ssize_t __write_ports_names(char *buf, struct net *net) |
0a5372d8 | 678 | { |
9dd9845f SK |
679 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
680 | ||
681 | if (nn->nfsd_serv == NULL) | |
0a5372d8 | 682 | return 0; |
9dd9845f | 683 | return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); |
0a5372d8 CL |
684 | } |
685 | ||
0b7c2f6f CL |
686 | /* |
687 | * A single 'fd' number was written, in which case it must be for | |
688 | * a socket of a supported family/protocol, and we use it as an | |
689 | * nfsd listener. | |
690 | */ | |
4df493a2 | 691 | static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred) |
0b7c2f6f CL |
692 | { |
693 | char *mesg = buf; | |
694 | int fd, err; | |
9dd9845f | 695 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
64e63041 | 696 | struct svc_serv *serv; |
0b7c2f6f CL |
697 | |
698 | err = get_int(&mesg, &fd); | |
699 | if (err != 0 || fd < 0) | |
700 | return -EINVAL; | |
39d432fc | 701 | trace_nfsd_ctl_ports_addfd(net, fd); |
0b7c2f6f | 702 | |
6777436b | 703 | err = nfsd_create_serv(net); |
0b7c2f6f CL |
704 | if (err != 0) |
705 | return err; | |
706 | ||
64e63041 JL |
707 | serv = nn->nfsd_serv; |
708 | err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); | |
0b7c2f6f | 709 | |
1e3577a4 | 710 | if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) |
17419aef N |
711 | nfsd_destroy_serv(net); |
712 | ||
ea068bad | 713 | return err; |
0b7c2f6f CL |
714 | } |
715 | ||
4eb68c26 | 716 | /* |
442a6290 | 717 | * A transport listener is added by writing its transport name and |
4eb68c26 CL |
718 | * a port number. |
719 | */ | |
4df493a2 | 720 | static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred) |
4eb68c26 CL |
721 | { |
722 | char transport[16]; | |
37498292 | 723 | struct svc_xprt *xprt; |
4eb68c26 | 724 | int port, err; |
9dd9845f | 725 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
64e63041 | 726 | struct svc_serv *serv; |
4eb68c26 | 727 | |
a10fded1 | 728 | if (sscanf(buf, "%15s %5u", transport, &port) != 2) |
4eb68c26 CL |
729 | return -EINVAL; |
730 | ||
4be929be | 731 | if (port < 1 || port > USHRT_MAX) |
4eb68c26 | 732 | return -EINVAL; |
39d432fc | 733 | trace_nfsd_ctl_ports_addxprt(net, transport, port); |
4eb68c26 | 734 | |
6777436b | 735 | err = nfsd_create_serv(net); |
4eb68c26 CL |
736 | if (err != 0) |
737 | return err; | |
738 | ||
64e63041 JL |
739 | serv = nn->nfsd_serv; |
740 | err = svc_xprt_create(serv, transport, net, | |
352ad314 | 741 | PF_INET, port, SVC_SOCK_ANONYMOUS, cred); |
68717908 | 742 | if (err < 0) |
37498292 CL |
743 | goto out_err; |
744 | ||
64e63041 | 745 | err = svc_xprt_create(serv, transport, net, |
352ad314 | 746 | PF_INET6, port, SVC_SOCK_ANONYMOUS, cred); |
37498292 CL |
747 | if (err < 0 && err != -EAFNOSUPPORT) |
748 | goto out_close; | |
0cd14a06 | 749 | |
4eb68c26 | 750 | return 0; |
37498292 | 751 | out_close: |
64e63041 | 752 | xprt = svc_find_xprt(serv, transport, net, PF_INET, port); |
37498292 | 753 | if (xprt != NULL) { |
4355d767 | 754 | svc_xprt_close(xprt); |
37498292 CL |
755 | svc_xprt_put(xprt); |
756 | } | |
757 | out_err: | |
1e3577a4 | 758 | if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) |
17419aef | 759 | nfsd_destroy_serv(net); |
2a501f55 | 760 | |
37498292 | 761 | return err; |
4eb68c26 CL |
762 | } |
763 | ||
08160352 SK |
764 | static ssize_t __write_ports(struct file *file, char *buf, size_t size, |
765 | struct net *net) | |
80212d59 | 766 | { |
0a5372d8 | 767 | if (size == 0) |
9dd9845f | 768 | return __write_ports_names(buf, net); |
0b7c2f6f CL |
769 | |
770 | if (isdigit(buf[0])) | |
4df493a2 | 771 | return __write_ports_addfd(buf, net, file->f_cred); |
82d56591 | 772 | |
4eb68c26 | 773 | if (isalpha(buf[0])) |
4df493a2 | 774 | return __write_ports_addxprt(buf, net, file->f_cred); |
4cd5dc75 | 775 | |
b41b66d6 | 776 | return -EINVAL; |
80212d59 N |
777 | } |
778 | ||
f2453978 | 779 | /* |
262a0982 CL |
780 | * write_ports - Pass a socket file descriptor or transport name to listen on |
781 | * | |
782 | * Input: | |
783 | * buf: ignored | |
784 | * size: zero | |
785 | * Output: | |
786 | * On success: passed-in buffer filled with a '\n'-terminated C | |
787 | * string containing a whitespace-separated list of | |
788 | * named NFSD listeners; | |
789 | * return code is the size in bytes of the string | |
790 | * On error: return code is zero or a negative errno value | |
791 | * | |
792 | * OR | |
793 | * | |
794 | * Input: | |
795 | * buf: C string containing an unsigned | |
796 | * integer value representing a bound | |
797 | * but unconnected socket that is to be | |
c71206a7 CL |
798 | * used as an NFSD listener; listen(3) |
799 | * must be called for a SOCK_STREAM | |
800 | * socket, otherwise it is ignored | |
262a0982 CL |
801 | * size: non-zero length of C string in @buf |
802 | * Output: | |
803 | * On success: NFS service is started; | |
804 | * passed-in buffer filled with a '\n'-terminated C | |
805 | * string containing a unique alphanumeric name of | |
806 | * the listener; | |
807 | * return code is the size in bytes of the string | |
808 | * On error: return code is a negative errno value | |
809 | * | |
810 | * OR | |
811 | * | |
812 | * Input: | |
262a0982 CL |
813 | * buf: C string containing a transport |
814 | * name and an unsigned integer value | |
815 | * representing the port to listen on, | |
816 | * separated by whitespace | |
817 | * size: non-zero length of C string in @buf | |
818 | * Output: | |
819 | * On success: returns zero; NFS service is started | |
820 | * On error: return code is a negative errno value | |
262a0982 | 821 | */ |
bedbdd8b NB |
822 | static ssize_t write_ports(struct file *file, char *buf, size_t size) |
823 | { | |
824 | ssize_t rv; | |
3dd98a3b | 825 | |
bedbdd8b | 826 | mutex_lock(&nfsd_mutex); |
244c7d44 | 827 | rv = __write_ports(file, buf, size, netns(file)); |
bedbdd8b NB |
828 | mutex_unlock(&nfsd_mutex); |
829 | return rv; | |
830 | } | |
831 | ||
832 | ||
596bbe53 N |
833 | int nfsd_max_blksize; |
834 | ||
f2453978 | 835 | /* |
262a0982 CL |
836 | * write_maxblksize - Set or report the current NFS blksize |
837 | * | |
838 | * Input: | |
839 | * buf: ignored | |
840 | * size: zero | |
841 | * | |
842 | * OR | |
843 | * | |
844 | * Input: | |
442a6290 CL |
845 | * buf: C string containing an unsigned |
846 | * integer value representing the new | |
847 | * NFS blksize | |
262a0982 CL |
848 | * size: non-zero length of C string in @buf |
849 | * Output: | |
850 | * On success: passed-in buffer filled with '\n'-terminated C string | |
851 | * containing numeric value of the current NFS blksize | |
852 | * setting; | |
853 | * return code is the size in bytes of the string | |
854 | * On error: return code is zero or a negative errno value | |
855 | */ | |
596bbe53 N |
856 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) |
857 | { | |
858 | char *mesg = buf; | |
244c7d44 | 859 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 860 | |
596bbe53 N |
861 | if (size > 0) { |
862 | int bsize; | |
863 | int rv = get_int(&mesg, &bsize); | |
864 | if (rv) | |
865 | return rv; | |
39d432fc CL |
866 | trace_nfsd_ctl_maxblksize(netns(file), bsize); |
867 | ||
596bbe53 N |
868 | /* force bsize into allowed range and |
869 | * required alignment. | |
870 | */ | |
3c7aa15d KM |
871 | bsize = max_t(int, bsize, 1024); |
872 | bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE); | |
596bbe53 | 873 | bsize &= ~(1024-1); |
bedbdd8b | 874 | mutex_lock(&nfsd_mutex); |
9dd9845f | 875 | if (nn->nfsd_serv) { |
bedbdd8b | 876 | mutex_unlock(&nfsd_mutex); |
596bbe53 N |
877 | return -EBUSY; |
878 | } | |
879 | nfsd_max_blksize = bsize; | |
bedbdd8b | 880 | mutex_unlock(&nfsd_mutex); |
596bbe53 | 881 | } |
e06b6405 CL |
882 | |
883 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", | |
884 | nfsd_max_blksize); | |
596bbe53 N |
885 | } |
886 | ||
f2453978 | 887 | /* |
5b8db00b JL |
888 | * write_maxconn - Set or report the current max number of connections |
889 | * | |
890 | * Input: | |
891 | * buf: ignored | |
892 | * size: zero | |
893 | * OR | |
894 | * | |
895 | * Input: | |
442a6290 CL |
896 | * buf: C string containing an unsigned |
897 | * integer value representing the new | |
898 | * number of max connections | |
5b8db00b JL |
899 | * size: non-zero length of C string in @buf |
900 | * Output: | |
901 | * On success: passed-in buffer filled with '\n'-terminated C string | |
902 | * containing numeric value of max_connections setting | |
903 | * for this net namespace; | |
904 | * return code is the size in bytes of the string | |
905 | * On error: return code is zero or a negative errno value | |
906 | */ | |
907 | static ssize_t write_maxconn(struct file *file, char *buf, size_t size) | |
908 | { | |
909 | char *mesg = buf; | |
244c7d44 | 910 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
5b8db00b JL |
911 | unsigned int maxconn = nn->max_connections; |
912 | ||
913 | if (size > 0) { | |
914 | int rv = get_uint(&mesg, &maxconn); | |
915 | ||
916 | if (rv) | |
917 | return rv; | |
39d432fc | 918 | trace_nfsd_ctl_maxconn(netns(file), maxconn); |
5b8db00b JL |
919 | nn->max_connections = maxconn; |
920 | } | |
921 | ||
922 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%u\n", maxconn); | |
923 | } | |
924 | ||
70c3b76c | 925 | #ifdef CONFIG_NFSD_V4 |
9dd9845f | 926 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, |
20b7d86f | 927 | time64_t *time, struct nfsd_net *nn) |
1da177e4 | 928 | { |
39d432fc | 929 | struct dentry *dentry = file_dentry(file); |
1da177e4 | 930 | char *mesg = buf; |
f0135740 | 931 | int rv, i; |
1da177e4 LT |
932 | |
933 | if (size > 0) { | |
9dd9845f | 934 | if (nn->nfsd_serv) |
3dd98a3b | 935 | return -EBUSY; |
f0135740 | 936 | rv = get_int(&mesg, &i); |
1da177e4 LT |
937 | if (rv) |
938 | return rv; | |
39d432fc CL |
939 | trace_nfsd_ctl_time(netns(file), dentry->d_name.name, |
940 | dentry->d_name.len, i); | |
941 | ||
e7b184f1 BF |
942 | /* |
943 | * Some sanity checking. We don't have a reason for | |
944 | * these particular numbers, but problems with the | |
945 | * extremes are: | |
946 | * - Too short: the briefest network outage may | |
947 | * cause clients to lose all their locks. Also, | |
948 | * the frequent polling may be wasteful. | |
949 | * - Too long: do you really want reboot recovery | |
950 | * to take more than an hour? Or to make other | |
951 | * clients wait an hour before being able to | |
952 | * revoke a dead client's locks? | |
953 | */ | |
f0135740 | 954 | if (i < 10 || i > 3600) |
1da177e4 | 955 | return -EINVAL; |
f0135740 | 956 | *time = i; |
1da177e4 | 957 | } |
e06b6405 | 958 | |
20b7d86f | 959 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time); |
f0135740 BF |
960 | } |
961 | ||
9dd9845f | 962 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, |
20b7d86f | 963 | time64_t *time, struct nfsd_net *nn) |
f0135740 BF |
964 | { |
965 | ssize_t rv; | |
966 | ||
967 | mutex_lock(&nfsd_mutex); | |
9dd9845f | 968 | rv = __nfsd4_write_time(file, buf, size, time, nn); |
f0135740 BF |
969 | mutex_unlock(&nfsd_mutex); |
970 | return rv; | |
1da177e4 LT |
971 | } |
972 | ||
f2453978 | 973 | /* |
262a0982 CL |
974 | * write_leasetime - Set or report the current NFSv4 lease time |
975 | * | |
976 | * Input: | |
977 | * buf: ignored | |
978 | * size: zero | |
979 | * | |
980 | * OR | |
981 | * | |
982 | * Input: | |
983 | * buf: C string containing an unsigned | |
984 | * integer value representing the new | |
985 | * NFSv4 lease expiry time | |
986 | * size: non-zero length of C string in @buf | |
987 | * Output: | |
988 | * On success: passed-in buffer filled with '\n'-terminated C | |
989 | * string containing unsigned integer value of the | |
990 | * current lease expiry time; | |
991 | * return code is the size in bytes of the string | |
992 | * On error: return code is zero or a negative errno value | |
993 | */ | |
3dd98a3b JL |
994 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) |
995 | { | |
244c7d44 | 996 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 997 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); |
3dd98a3b JL |
998 | } |
999 | ||
f2453978 | 1000 | /* |
efc4bb4f BF |
1001 | * write_gracetime - Set or report current NFSv4 grace period time |
1002 | * | |
1003 | * As above, but sets the time of the NFSv4 grace period. | |
1004 | * | |
1005 | * Note this should never be set to less than the *previous* | |
1006 | * lease-period time, but we don't try to enforce this. (In the common | |
1007 | * case (a new boot), we don't know what the previous lease time was | |
1008 | * anyway.) | |
1009 | */ | |
1010 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size) | |
1011 | { | |
244c7d44 | 1012 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 1013 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); |
efc4bb4f BF |
1014 | } |
1015 | ||
74fd4873 | 1016 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
9dd9845f SK |
1017 | static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, |
1018 | struct nfsd_net *nn) | |
0964a3d3 N |
1019 | { |
1020 | char *mesg = buf; | |
1021 | char *recdir; | |
1022 | int len, status; | |
1023 | ||
3dd98a3b | 1024 | if (size > 0) { |
9dd9845f | 1025 | if (nn->nfsd_serv) |
3dd98a3b JL |
1026 | return -EBUSY; |
1027 | if (size > PATH_MAX || buf[size-1] != '\n') | |
1028 | return -EINVAL; | |
1029 | buf[size-1] = 0; | |
0964a3d3 | 1030 | |
3dd98a3b JL |
1031 | recdir = mesg; |
1032 | len = qword_get(&mesg, recdir, size); | |
1033 | if (len <= 0) | |
1034 | return -EINVAL; | |
39d432fc | 1035 | trace_nfsd_ctl_recoverydir(netns(file), recdir); |
0964a3d3 | 1036 | |
3dd98a3b | 1037 | status = nfs4_reset_recoverydir(recdir); |
69049961 AK |
1038 | if (status) |
1039 | return status; | |
3dd98a3b | 1040 | } |
3d72ab8f CL |
1041 | |
1042 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n", | |
1043 | nfs4_recoverydir()); | |
0964a3d3 | 1044 | } |
3dd98a3b | 1045 | |
f2453978 | 1046 | /* |
262a0982 CL |
1047 | * write_recoverydir - Set or report the pathname of the recovery directory |
1048 | * | |
1049 | * Input: | |
1050 | * buf: ignored | |
1051 | * size: zero | |
1052 | * | |
1053 | * OR | |
1054 | * | |
1055 | * Input: | |
1056 | * buf: C string containing the pathname | |
1057 | * of the directory on a local file | |
1058 | * system containing permanent NFSv4 | |
1059 | * recovery data | |
1060 | * size: non-zero length of C string in @buf | |
1061 | * Output: | |
1062 | * On success: passed-in buffer filled with '\n'-terminated C string | |
1063 | * containing the current recovery pathname setting; | |
1064 | * return code is the size in bytes of the string | |
1065 | * On error: return code is zero or a negative errno value | |
1066 | */ | |
3dd98a3b JL |
1067 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) |
1068 | { | |
1069 | ssize_t rv; | |
244c7d44 | 1070 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
3dd98a3b JL |
1071 | |
1072 | mutex_lock(&nfsd_mutex); | |
9dd9845f | 1073 | rv = __write_recoverydir(file, buf, size, nn); |
3dd98a3b JL |
1074 | mutex_unlock(&nfsd_mutex); |
1075 | return rv; | |
1076 | } | |
74fd4873 | 1077 | #endif |
3dd98a3b | 1078 | |
f2453978 | 1079 | /* |
7f5ef2e9 JL |
1080 | * write_v4_end_grace - release grace period for nfsd's v4.x lock manager |
1081 | * | |
1082 | * Input: | |
1083 | * buf: ignored | |
1084 | * size: zero | |
1085 | * OR | |
1086 | * | |
1087 | * Input: | |
442a6290 | 1088 | * buf: any value |
7f5ef2e9 JL |
1089 | * size: non-zero length of C string in @buf |
1090 | * Output: | |
1091 | * passed-in buffer filled with "Y" or "N" with a newline | |
1092 | * and NULL-terminated C string. This indicates whether | |
1093 | * the grace period has ended in the current net | |
1094 | * namespace. Return code is the size in bytes of the | |
1095 | * string. Writing a string that starts with 'Y', 'y', or | |
1096 | * '1' to the file will end the grace period for nfsd's v4 | |
1097 | * lock manager. | |
1098 | */ | |
1099 | static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size) | |
1100 | { | |
244c7d44 | 1101 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
7f5ef2e9 JL |
1102 | |
1103 | if (size > 0) { | |
1104 | switch(buf[0]) { | |
1105 | case 'Y': | |
1106 | case 'y': | |
1107 | case '1': | |
dd838821 | 1108 | if (!nn->nfsd_serv) |
62a063b8 | 1109 | return -EBUSY; |
39d432fc | 1110 | trace_nfsd_end_grace(netns(file)); |
8073a98e | 1111 | nfsd4_end_grace(nn); |
7f5ef2e9 JL |
1112 | break; |
1113 | default: | |
1114 | return -EINVAL; | |
1115 | } | |
1116 | } | |
1117 | ||
1118 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n", | |
1119 | nn->grace_ended ? 'Y' : 'N'); | |
1120 | } | |
1121 | ||
70c3b76c | 1122 | #endif |
0964a3d3 | 1123 | |
1da177e4 LT |
1124 | /*----------------------------------------------------------------------------*/ |
1125 | /* | |
1126 | * populating the filesystem. | |
1127 | */ | |
1128 | ||
e8a79fb1 BF |
1129 | /* Basically copying rpc_get_inode. */ |
1130 | static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode) | |
1131 | { | |
1132 | struct inode *inode = new_inode(sb); | |
1133 | if (!inode) | |
1134 | return NULL; | |
1135 | /* Following advice from simple_fill_super documentation: */ | |
1136 | inode->i_ino = iunique(sb, NFSD_MaxReserved); | |
1137 | inode->i_mode = mode; | |
11fec9b9 | 1138 | simple_inode_init_ts(inode); |
e8a79fb1 BF |
1139 | switch (mode & S_IFMT) { |
1140 | case S_IFDIR: | |
1141 | inode->i_fop = &simple_dir_operations; | |
1142 | inode->i_op = &simple_dir_inode_operations; | |
1143 | inc_nlink(inode); | |
76c50eb7 | 1144 | break; |
4df750c9 CL |
1145 | case S_IFLNK: |
1146 | inode->i_op = &simple_symlink_inode_operations; | |
1147 | break; | |
e8a79fb1 BF |
1148 | default: |
1149 | break; | |
1150 | } | |
1151 | return inode; | |
1152 | } | |
1153 | ||
bebd6997 | 1154 | static int __nfsd_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode, struct nfsdfs_client *ncl) |
e8a79fb1 BF |
1155 | { |
1156 | struct inode *inode; | |
1157 | ||
1158 | inode = nfsd_get_inode(dir->i_sb, mode); | |
1159 | if (!inode) | |
1160 | return -ENOMEM; | |
bebd6997 BF |
1161 | if (ncl) { |
1162 | inode->i_private = ncl; | |
1163 | kref_get(&ncl->cl_ref); | |
1164 | } | |
e8a79fb1 BF |
1165 | d_add(dentry, inode); |
1166 | inc_nlink(dir); | |
1167 | fsnotify_mkdir(dir, dentry); | |
1168 | return 0; | |
1169 | } | |
1170 | ||
1171 | static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name) | |
1172 | { | |
1173 | struct inode *dir = parent->d_inode; | |
1174 | struct dentry *dentry; | |
1175 | int ret = -ENOMEM; | |
1176 | ||
1177 | inode_lock(dir); | |
1178 | dentry = d_alloc_name(parent, name); | |
1179 | if (!dentry) | |
1180 | goto out_err; | |
bebd6997 | 1181 | ret = __nfsd_mkdir(d_inode(parent), dentry, S_IFDIR | 0600, ncl); |
e8a79fb1 BF |
1182 | if (ret) |
1183 | goto out_err; | |
e8a79fb1 BF |
1184 | out: |
1185 | inode_unlock(dir); | |
1186 | return dentry; | |
1187 | out_err: | |
d6846bfb | 1188 | dput(dentry); |
e8a79fb1 BF |
1189 | dentry = ERR_PTR(ret); |
1190 | goto out; | |
1191 | } | |
1192 | ||
4df750c9 CL |
1193 | #if IS_ENABLED(CONFIG_SUNRPC_GSS) |
1194 | static int __nfsd_symlink(struct inode *dir, struct dentry *dentry, | |
1195 | umode_t mode, const char *content) | |
1196 | { | |
1197 | struct inode *inode; | |
1198 | ||
1199 | inode = nfsd_get_inode(dir->i_sb, mode); | |
1200 | if (!inode) | |
1201 | return -ENOMEM; | |
1202 | ||
1203 | inode->i_link = (char *)content; | |
1204 | inode->i_size = strlen(content); | |
1205 | ||
1206 | d_add(dentry, inode); | |
1207 | inc_nlink(dir); | |
1208 | fsnotify_create(dir, dentry); | |
1209 | return 0; | |
1210 | } | |
1211 | ||
1212 | /* | |
1213 | * @content is assumed to be a NUL-terminated string that lives | |
1214 | * longer than the symlink itself. | |
1215 | */ | |
39d432fc CL |
1216 | static void _nfsd_symlink(struct dentry *parent, const char *name, |
1217 | const char *content) | |
4df750c9 CL |
1218 | { |
1219 | struct inode *dir = parent->d_inode; | |
1220 | struct dentry *dentry; | |
4b471a8b | 1221 | int ret; |
4df750c9 CL |
1222 | |
1223 | inode_lock(dir); | |
1224 | dentry = d_alloc_name(parent, name); | |
1225 | if (!dentry) | |
4b471a8b | 1226 | goto out; |
4df750c9 CL |
1227 | ret = __nfsd_symlink(d_inode(parent), dentry, S_IFLNK | 0777, content); |
1228 | if (ret) | |
4b471a8b | 1229 | dput(dentry); |
4df750c9 CL |
1230 | out: |
1231 | inode_unlock(dir); | |
4df750c9 CL |
1232 | } |
1233 | #else | |
39d432fc CL |
1234 | static inline void _nfsd_symlink(struct dentry *parent, const char *name, |
1235 | const char *content) | |
4df750c9 CL |
1236 | { |
1237 | } | |
1238 | ||
1239 | #endif | |
1240 | ||
b7a14708 | 1241 | static void clear_ncl(struct dentry *dentry) |
97ad4031 | 1242 | { |
b7a14708 | 1243 | struct inode *inode = d_inode(dentry); |
97ad4031 BF |
1244 | struct nfsdfs_client *ncl = inode->i_private; |
1245 | ||
b7a14708 | 1246 | spin_lock(&inode->i_lock); |
97ad4031 | 1247 | inode->i_private = NULL; |
b7a14708 | 1248 | spin_unlock(&inode->i_lock); |
97ad4031 BF |
1249 | kref_put(&ncl->cl_ref, ncl->cl_release); |
1250 | } | |
1251 | ||
97ad4031 BF |
1252 | struct nfsdfs_client *get_nfsdfs_client(struct inode *inode) |
1253 | { | |
1254 | struct nfsdfs_client *nc; | |
1255 | ||
b7a14708 AV |
1256 | spin_lock(&inode->i_lock); |
1257 | nc = inode->i_private; | |
1258 | if (nc) | |
1259 | kref_get(&nc->cl_ref); | |
1260 | spin_unlock(&inode->i_lock); | |
97ad4031 BF |
1261 | return nc; |
1262 | } | |
97ad4031 BF |
1263 | |
1264 | /* XXX: cut'n'paste from simple_fill_super; figure out if we could share | |
1265 | * code instead. */ | |
1266 | static int nfsdfs_create_files(struct dentry *root, | |
472d155a | 1267 | const struct tree_descr *files, |
b7a14708 | 1268 | struct nfsdfs_client *ncl, |
472d155a | 1269 | struct dentry **fdentries) |
97ad4031 BF |
1270 | { |
1271 | struct inode *dir = d_inode(root); | |
1272 | struct inode *inode; | |
1273 | struct dentry *dentry; | |
1274 | int i; | |
1275 | ||
1276 | inode_lock(dir); | |
1277 | for (i = 0; files->name && files->name[0]; i++, files++) { | |
97ad4031 BF |
1278 | dentry = d_alloc_name(root, files->name); |
1279 | if (!dentry) | |
1280 | goto out; | |
1281 | inode = nfsd_get_inode(d_inode(root)->i_sb, | |
1282 | S_IFREG | files->mode); | |
1283 | if (!inode) { | |
1284 | dput(dentry); | |
1285 | goto out; | |
1286 | } | |
b7a14708 | 1287 | kref_get(&ncl->cl_ref); |
97ad4031 | 1288 | inode->i_fop = files->ops; |
b7a14708 | 1289 | inode->i_private = ncl; |
97ad4031 BF |
1290 | d_add(dentry, inode); |
1291 | fsnotify_create(dir, dentry); | |
472d155a N |
1292 | if (fdentries) |
1293 | fdentries[i] = dentry; | |
97ad4031 BF |
1294 | } |
1295 | inode_unlock(dir); | |
1296 | return 0; | |
1297 | out: | |
97ad4031 BF |
1298 | inode_unlock(dir); |
1299 | return -ENOMEM; | |
1300 | } | |
1301 | ||
e8a79fb1 | 1302 | /* on success, returns positive number unique to that client. */ |
97ad4031 | 1303 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, |
472d155a N |
1304 | struct nfsdfs_client *ncl, u32 id, |
1305 | const struct tree_descr *files, | |
1306 | struct dentry **fdentries) | |
e8a79fb1 | 1307 | { |
97ad4031 | 1308 | struct dentry *dentry; |
e8a79fb1 | 1309 | char name[11]; |
97ad4031 | 1310 | int ret; |
e8a79fb1 | 1311 | |
bf5ed3e3 | 1312 | sprintf(name, "%u", id); |
e8a79fb1 | 1313 | |
97ad4031 BF |
1314 | dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); |
1315 | if (IS_ERR(dentry)) /* XXX: tossing errors? */ | |
1316 | return NULL; | |
b7a14708 | 1317 | ret = nfsdfs_create_files(dentry, files, ncl, fdentries); |
97ad4031 BF |
1318 | if (ret) { |
1319 | nfsd_client_rmdir(dentry); | |
1320 | return NULL; | |
1321 | } | |
1322 | return dentry; | |
e8a79fb1 BF |
1323 | } |
1324 | ||
1325 | /* Taken from __rpc_rmdir: */ | |
1326 | void nfsd_client_rmdir(struct dentry *dentry) | |
1327 | { | |
b7a14708 | 1328 | simple_recursive_removal(dentry, clear_ncl); |
e8a79fb1 BF |
1329 | } |
1330 | ||
96a374a3 | 1331 | static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) |
1da177e4 | 1332 | { |
e8a79fb1 BF |
1333 | struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, |
1334 | nfsd_net_id); | |
1335 | struct dentry *dentry; | |
1336 | int ret; | |
1337 | ||
cda37124 | 1338 | static const struct tree_descr nfsd_files[] = { |
96d851c4 | 1339 | [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, |
20ad856e AG |
1340 | /* Per-export io stats use same ops as exports file */ |
1341 | [NFSD_Export_Stats] = {"export_stats", &exports_nfsd_operations, S_IRUGO}, | |
e8e8753f | 1342 | [NFSD_Export_features] = {"export_features", |
9beeaab8 | 1343 | &export_features_fops, S_IRUGO}, |
4373ea84 WC |
1344 | [NFSD_FO_UnlockIP] = {"unlock_ip", |
1345 | &transaction_ops, S_IWUSR|S_IRUSR}, | |
17efa372 WC |
1346 | [NFSD_FO_UnlockFS] = {"unlock_filesystem", |
1347 | &transaction_ops, S_IWUSR|S_IRUSR}, | |
1da177e4 LT |
1348 | [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, |
1349 | [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, | |
eed2965a | 1350 | [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, |
03cf6c9f | 1351 | [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO}, |
64776611 C |
1352 | [NFSD_Reply_Cache_Stats] = {"reply_cache_stats", |
1353 | &nfsd_reply_cache_stats_fops, S_IRUGO}, | |
70c3b76c | 1354 | [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, |
80212d59 | 1355 | [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, |
596bbe53 | 1356 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, |
5b8db00b | 1357 | [NFSD_MaxConnections] = {"max_connections", &transaction_ops, S_IWUSR|S_IRUGO}, |
1342f9dd | 1358 | [NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO}, |
1da177e4 LT |
1359 | #ifdef CONFIG_NFSD_V4 |
1360 | [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, | |
efc4bb4f | 1361 | [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, |
0964a3d3 | 1362 | [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, |
7f5ef2e9 | 1363 | [NFSD_V4EndGrace] = {"v4_end_grace", &transaction_ops, S_IWUSR|S_IRUGO}, |
1da177e4 LT |
1364 | #endif |
1365 | /* last one */ {""} | |
1366 | }; | |
96a374a3 | 1367 | |
e8a79fb1 BF |
1368 | ret = simple_fill_super(sb, 0x6e667364, nfsd_files); |
1369 | if (ret) | |
1370 | return ret; | |
39d432fc CL |
1371 | _nfsd_symlink(sb->s_root, "supported_krb5_enctypes", |
1372 | "/proc/net/rpc/gss_krb5_enctypes"); | |
e8a79fb1 BF |
1373 | dentry = nfsd_mkdir(sb->s_root, NULL, "clients"); |
1374 | if (IS_ERR(dentry)) | |
1375 | return PTR_ERR(dentry); | |
1376 | nn->nfsd_client_dir = dentry; | |
1377 | return 0; | |
1da177e4 | 1378 | } |
e8a79fb1 | 1379 | |
96a374a3 DH |
1380 | static int nfsd_fs_get_tree(struct fs_context *fc) |
1381 | { | |
533770cc | 1382 | return get_tree_keyed(fc, nfsd_fill_super, get_net(fc->net_ns)); |
1da177e4 LT |
1383 | } |
1384 | ||
96a374a3 | 1385 | static void nfsd_fs_free_fc(struct fs_context *fc) |
1da177e4 | 1386 | { |
96a374a3 DH |
1387 | if (fc->s_fs_info) |
1388 | put_net(fc->s_fs_info); | |
1389 | } | |
1390 | ||
1391 | static const struct fs_context_operations nfsd_fs_context_ops = { | |
1392 | .free = nfsd_fs_free_fc, | |
1393 | .get_tree = nfsd_fs_get_tree, | |
1394 | }; | |
1395 | ||
1396 | static int nfsd_init_fs_context(struct fs_context *fc) | |
1da177e4 | 1397 | { |
96a374a3 DH |
1398 | put_user_ns(fc->user_ns); |
1399 | fc->user_ns = get_user_ns(fc->net_ns->user_ns); | |
1400 | fc->ops = &nfsd_fs_context_ops; | |
1401 | return 0; | |
11f77942 SK |
1402 | } |
1403 | ||
1404 | static void nfsd_umount(struct super_block *sb) | |
1405 | { | |
1406 | struct net *net = sb->s_fs_info; | |
1407 | ||
c6c7f2a8 TM |
1408 | nfsd_shutdown_threads(net); |
1409 | ||
11f77942 SK |
1410 | kill_litter_super(sb); |
1411 | put_net(net); | |
1da177e4 LT |
1412 | } |
1413 | ||
1414 | static struct file_system_type nfsd_fs_type = { | |
1415 | .owner = THIS_MODULE, | |
1416 | .name = "nfsd", | |
96a374a3 | 1417 | .init_fs_context = nfsd_init_fs_context, |
11f77942 | 1418 | .kill_sb = nfsd_umount, |
1da177e4 | 1419 | }; |
7f78e035 | 1420 | MODULE_ALIAS_FS("nfsd"); |
1da177e4 | 1421 | |
e331f606 | 1422 | #ifdef CONFIG_PROC_FS |
340086da TR |
1423 | |
1424 | static int exports_proc_open(struct inode *inode, struct file *file) | |
1425 | { | |
1426 | return exports_net_open(current->nsproxy->net_ns, file); | |
1427 | } | |
1428 | ||
1429 | static const struct proc_ops exports_proc_ops = { | |
1430 | .proc_open = exports_proc_open, | |
1431 | .proc_read = seq_read, | |
1432 | .proc_lseek = seq_lseek, | |
1433 | .proc_release = seq_release, | |
1434 | }; | |
1435 | ||
e331f606 BF |
1436 | static int create_proc_exports_entry(void) |
1437 | { | |
1438 | struct proc_dir_entry *entry; | |
1439 | ||
1440 | entry = proc_mkdir("fs/nfs", NULL); | |
1441 | if (!entry) | |
1442 | return -ENOMEM; | |
97a32539 | 1443 | entry = proc_create("exports", 0, entry, &exports_proc_ops); |
ff7c4b36 | 1444 | if (!entry) { |
1445 | remove_proc_entry("fs/nfs", NULL); | |
e331f606 | 1446 | return -ENOMEM; |
ff7c4b36 | 1447 | } |
e331f606 BF |
1448 | return 0; |
1449 | } | |
1450 | #else /* CONFIG_PROC_FS */ | |
1451 | static int create_proc_exports_entry(void) | |
1452 | { | |
1453 | return 0; | |
1454 | } | |
1455 | #endif | |
1456 | ||
c7d03a00 | 1457 | unsigned int nfsd_net_id; |
5717e012 | 1458 | |
bd9d6a3e LB |
1459 | /** |
1460 | * nfsd_nl_rpc_status_get_start - Prepare rpc_status_get dumpit | |
1461 | * @cb: netlink metadata and command arguments | |
1462 | * | |
1463 | * Return values: | |
1464 | * %0: The rpc_status_get command may proceed | |
1465 | * %-ENODEV: There is no NFSD running in this namespace | |
1466 | */ | |
13727f85 LB |
1467 | int nfsd_nl_rpc_status_get_start(struct netlink_callback *cb) |
1468 | { | |
bd9d6a3e LB |
1469 | struct nfsd_net *nn = net_generic(sock_net(cb->skb->sk), nfsd_net_id); |
1470 | int ret = -ENODEV; | |
1471 | ||
1472 | mutex_lock(&nfsd_mutex); | |
1bd773b4 | 1473 | if (nn->nfsd_serv) |
bd9d6a3e | 1474 | ret = 0; |
1bd773b4 N |
1475 | else |
1476 | mutex_unlock(&nfsd_mutex); | |
bd9d6a3e LB |
1477 | |
1478 | return ret; | |
1479 | } | |
1480 | ||
1481 | static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, | |
1482 | struct netlink_callback *cb, | |
1483 | struct nfsd_genl_rqstp *rqstp) | |
1484 | { | |
1485 | void *hdr; | |
1486 | u32 i; | |
1487 | ||
1488 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | |
1489 | &nfsd_nl_family, 0, NFSD_CMD_RPC_STATUS_GET); | |
1490 | if (!hdr) | |
1491 | return -ENOBUFS; | |
1492 | ||
1493 | if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, rqstp->rq_xid) || | |
1494 | nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, rqstp->rq_flags) || | |
1495 | nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, rqstp->rq_prog) || | |
1496 | nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, rqstp->rq_proc) || | |
1497 | nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, rqstp->rq_vers) || | |
1498 | nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME, | |
1499 | ktime_to_us(rqstp->rq_stime), | |
1500 | NFSD_A_RPC_STATUS_PAD)) | |
1501 | return -ENOBUFS; | |
1502 | ||
1503 | switch (rqstp->rq_saddr.sa_family) { | |
1504 | case AF_INET: { | |
1505 | const struct sockaddr_in *s_in, *d_in; | |
1506 | ||
1507 | s_in = (const struct sockaddr_in *)&rqstp->rq_saddr; | |
1508 | d_in = (const struct sockaddr_in *)&rqstp->rq_daddr; | |
1509 | if (nla_put_in_addr(skb, NFSD_A_RPC_STATUS_SADDR4, | |
1510 | s_in->sin_addr.s_addr) || | |
1511 | nla_put_in_addr(skb, NFSD_A_RPC_STATUS_DADDR4, | |
1512 | d_in->sin_addr.s_addr) || | |
1513 | nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT, | |
1514 | s_in->sin_port) || | |
1515 | nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, | |
1516 | d_in->sin_port)) | |
1517 | return -ENOBUFS; | |
1518 | break; | |
1519 | } | |
1520 | case AF_INET6: { | |
1521 | const struct sockaddr_in6 *s_in, *d_in; | |
1522 | ||
1523 | s_in = (const struct sockaddr_in6 *)&rqstp->rq_saddr; | |
1524 | d_in = (const struct sockaddr_in6 *)&rqstp->rq_daddr; | |
1525 | if (nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_SADDR6, | |
1526 | &s_in->sin6_addr) || | |
1527 | nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_DADDR6, | |
1528 | &d_in->sin6_addr) || | |
1529 | nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT, | |
1530 | s_in->sin6_port) || | |
1531 | nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, | |
1532 | d_in->sin6_port)) | |
1533 | return -ENOBUFS; | |
1534 | break; | |
1535 | } | |
1536 | } | |
1537 | ||
1538 | for (i = 0; i < rqstp->rq_opcnt; i++) | |
1539 | if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS, | |
1540 | rqstp->rq_opnum[i])) | |
1541 | return -ENOBUFS; | |
1542 | ||
1543 | genlmsg_end(skb, hdr); | |
13727f85 LB |
1544 | return 0; |
1545 | } | |
1546 | ||
bd9d6a3e LB |
1547 | /** |
1548 | * nfsd_nl_rpc_status_get_dumpit - Handle rpc_status_get dumpit | |
1549 | * @skb: reply buffer | |
1550 | * @cb: netlink metadata and command arguments | |
1551 | * | |
1552 | * Returns the size of the reply or a negative errno. | |
1553 | */ | |
13727f85 LB |
1554 | int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, |
1555 | struct netlink_callback *cb) | |
1556 | { | |
bd9d6a3e LB |
1557 | struct nfsd_net *nn = net_generic(sock_net(skb->sk), nfsd_net_id); |
1558 | int i, ret, rqstp_index = 0; | |
1559 | ||
1560 | rcu_read_lock(); | |
1561 | ||
1562 | for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) { | |
1563 | struct svc_rqst *rqstp; | |
1564 | ||
1565 | if (i < cb->args[0]) /* already consumed */ | |
1566 | continue; | |
1567 | ||
1568 | rqstp_index = 0; | |
1569 | list_for_each_entry_rcu(rqstp, | |
1570 | &nn->nfsd_serv->sv_pools[i].sp_all_threads, | |
1571 | rq_all) { | |
1572 | struct nfsd_genl_rqstp genl_rqstp; | |
1573 | unsigned int status_counter; | |
1574 | ||
1575 | if (rqstp_index++ < cb->args[1]) /* already consumed */ | |
1576 | continue; | |
1577 | /* | |
1578 | * Acquire rq_status_counter before parsing the rqst | |
1579 | * fields. rq_status_counter is set to an odd value in | |
1580 | * order to notify the consumers the rqstp fields are | |
1581 | * meaningful. | |
1582 | */ | |
1583 | status_counter = | |
1584 | smp_load_acquire(&rqstp->rq_status_counter); | |
1585 | if (!(status_counter & 1)) | |
1586 | continue; | |
1587 | ||
1588 | genl_rqstp.rq_xid = rqstp->rq_xid; | |
1589 | genl_rqstp.rq_flags = rqstp->rq_flags; | |
1590 | genl_rqstp.rq_vers = rqstp->rq_vers; | |
1591 | genl_rqstp.rq_prog = rqstp->rq_prog; | |
1592 | genl_rqstp.rq_proc = rqstp->rq_proc; | |
1593 | genl_rqstp.rq_stime = rqstp->rq_stime; | |
1594 | genl_rqstp.rq_opcnt = 0; | |
1595 | memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp), | |
1596 | sizeof(struct sockaddr)); | |
1597 | memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp), | |
1598 | sizeof(struct sockaddr)); | |
1599 | ||
1600 | #ifdef CONFIG_NFSD_V4 | |
1601 | if (rqstp->rq_vers == NFS4_VERSION && | |
1602 | rqstp->rq_proc == NFSPROC4_COMPOUND) { | |
1603 | /* NFSv4 compound */ | |
1604 | struct nfsd4_compoundargs *args; | |
1605 | int j; | |
1606 | ||
1607 | args = rqstp->rq_argp; | |
1608 | genl_rqstp.rq_opcnt = args->opcnt; | |
1609 | for (j = 0; j < genl_rqstp.rq_opcnt; j++) | |
1610 | genl_rqstp.rq_opnum[j] = | |
1611 | args->ops[j].opnum; | |
1612 | } | |
1613 | #endif /* CONFIG_NFSD_V4 */ | |
1614 | ||
1615 | /* | |
1616 | * Acquire rq_status_counter before reporting the rqst | |
1617 | * fields to the user. | |
1618 | */ | |
1619 | if (smp_load_acquire(&rqstp->rq_status_counter) != | |
1620 | status_counter) | |
1621 | continue; | |
1622 | ||
1623 | ret = nfsd_genl_rpc_status_compose_msg(skb, cb, | |
1624 | &genl_rqstp); | |
1625 | if (ret) | |
1626 | goto out; | |
1627 | } | |
1628 | } | |
1629 | ||
1630 | cb->args[0] = i; | |
1631 | cb->args[1] = rqstp_index; | |
1632 | ret = skb->len; | |
1633 | out: | |
1634 | rcu_read_unlock(); | |
1635 | ||
1636 | return ret; | |
13727f85 LB |
1637 | } |
1638 | ||
bd9d6a3e LB |
1639 | /** |
1640 | * nfsd_nl_rpc_status_get_done - rpc_status_get dumpit post-processing | |
1641 | * @cb: netlink metadata and command arguments | |
1642 | * | |
1643 | * Return values: | |
1644 | * %0: Success | |
1645 | */ | |
13727f85 LB |
1646 | int nfsd_nl_rpc_status_get_done(struct netlink_callback *cb) |
1647 | { | |
bd9d6a3e LB |
1648 | mutex_unlock(&nfsd_mutex); |
1649 | ||
13727f85 LB |
1650 | return 0; |
1651 | } | |
1652 | ||
5e092be7 CL |
1653 | /** |
1654 | * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace | |
1655 | * @net: a freshly-created network namespace | |
1656 | * | |
1657 | * This information stays around as long as the network namespace is | |
1658 | * alive whether or not there is an NFSD instance running in the | |
1659 | * namespace. | |
1660 | * | |
1661 | * Returns zero on success, or a negative errno otherwise. | |
1662 | */ | |
1663 | static __net_init int nfsd_net_init(struct net *net) | |
5717e012 SK |
1664 | { |
1665 | int retval; | |
3d733711 | 1666 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
5717e012 SK |
1667 | |
1668 | retval = nfsd_export_init(net); | |
1669 | if (retval) | |
1670 | goto out_export_error; | |
f69adb2f SK |
1671 | retval = nfsd_idmap_init(net); |
1672 | if (retval) | |
1673 | goto out_idmap_error; | |
ed9ab734 JL |
1674 | retval = nfsd_net_reply_cache_init(nn); |
1675 | if (retval) | |
1676 | goto out_repcache_error; | |
e333f3bb TM |
1677 | nn->nfsd_versions = NULL; |
1678 | nn->nfsd4_minorversions = NULL; | |
f385f7d2 | 1679 | nfsd4_init_leases_net(nn); |
91d2e9b5 CL |
1680 | get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); |
1681 | seqlock_init(&nn->writeverf_lock); | |
2c830dd7 | 1682 | |
5717e012 SK |
1683 | return 0; |
1684 | ||
ed9ab734 JL |
1685 | out_repcache_error: |
1686 | nfsd_idmap_shutdown(net); | |
f69adb2f SK |
1687 | out_idmap_error: |
1688 | nfsd_export_shutdown(net); | |
5717e012 SK |
1689 | out_export_error: |
1690 | return retval; | |
1691 | } | |
1692 | ||
5e092be7 CL |
1693 | /** |
1694 | * nfsd_net_exit - Release the nfsd_net portion of a net namespace | |
1695 | * @net: a network namespace that is about to be destroyed | |
1696 | * | |
1697 | */ | |
1698 | static __net_exit void nfsd_net_exit(struct net *net) | |
5717e012 | 1699 | { |
ed9ab734 JL |
1700 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
1701 | ||
1702 | nfsd_net_reply_cache_destroy(nn); | |
f69adb2f | 1703 | nfsd_idmap_shutdown(net); |
5717e012 | 1704 | nfsd_export_shutdown(net); |
ed9ab734 | 1705 | nfsd_netns_free_versions(nn); |
5717e012 SK |
1706 | } |
1707 | ||
7ea34ac1 | 1708 | static struct pernet_operations nfsd_net_ops = { |
5e092be7 CL |
1709 | .init = nfsd_net_init, |
1710 | .exit = nfsd_net_exit, | |
7ea34ac1 JL |
1711 | .id = &nfsd_net_id, |
1712 | .size = sizeof(struct nfsd_net), | |
1713 | }; | |
1714 | ||
1da177e4 LT |
1715 | static int __init init_nfsd(void) |
1716 | { | |
1717 | int retval; | |
1da177e4 | 1718 | |
bb7ffbf2 GC |
1719 | retval = nfsd4_init_slabs(); |
1720 | if (retval) | |
b10252c7 | 1721 | return retval; |
9cf514cc | 1722 | retval = nfsd4_init_pnfs(); |
65178db4 BS |
1723 | if (retval) |
1724 | goto out_free_slabs; | |
e567b98c AG |
1725 | retval = nfsd_stat_init(); /* Statistics */ |
1726 | if (retval) | |
1727 | goto out_free_pnfs; | |
027690c7 BF |
1728 | retval = nfsd_drc_slab_create(); |
1729 | if (retval) | |
1730 | goto out_free_stat; | |
1da177e4 | 1731 | nfsd_lockd_init(); /* lockd->nfsd callbacks */ |
e331f606 BF |
1732 | retval = create_proc_exports_entry(); |
1733 | if (retval) | |
f69adb2f | 1734 | goto out_free_lockd; |
bd5ae928 BF |
1735 | retval = register_pernet_subsys(&nfsd_net_ops); |
1736 | if (retval < 0) | |
6f6f84aa | 1737 | goto out_free_exports; |
b10252c7 | 1738 | retval = register_cld_notifier(); |
d76cc46b | 1739 | if (retval) |
62fdb65e | 1740 | goto out_free_subsys; |
d76cc46b | 1741 | retval = nfsd4_create_laundry_wq(); |
6f6f84aa ZX |
1742 | if (retval) |
1743 | goto out_free_cld; | |
1744 | retval = register_filesystem(&nfsd_fs_type); | |
b10252c7 | 1745 | if (retval) |
26808d3f | 1746 | goto out_free_all; |
bd9d6a3e LB |
1747 | retval = genl_register_family(&nfsd_nl_family); |
1748 | if (retval) | |
1749 | goto out_free_all; | |
1750 | ||
26808d3f BF |
1751 | return 0; |
1752 | out_free_all: | |
6f6f84aa ZX |
1753 | nfsd4_destroy_laundry_wq(); |
1754 | out_free_cld: | |
62fdb65e ZX |
1755 | unregister_cld_notifier(); |
1756 | out_free_subsys: | |
b10252c7 | 1757 | unregister_pernet_subsys(&nfsd_net_ops); |
bd5ae928 | 1758 | out_free_exports: |
26808d3f BF |
1759 | remove_proc_entry("fs/nfs/exports", NULL); |
1760 | remove_proc_entry("fs/nfs", NULL); | |
dbf847ec | 1761 | out_free_lockd: |
26808d3f | 1762 | nfsd_lockd_shutdown(); |
027690c7 BF |
1763 | nfsd_drc_slab_free(); |
1764 | out_free_stat: | |
d5c3428b | 1765 | nfsd_stat_shutdown(); |
e567b98c | 1766 | out_free_pnfs: |
9cf514cc | 1767 | nfsd4_exit_pnfs(); |
65178db4 | 1768 | out_free_slabs: |
26808d3f | 1769 | nfsd4_free_slabs(); |
1da177e4 LT |
1770 | return retval; |
1771 | } | |
1772 | ||
1773 | static void __exit exit_nfsd(void) | |
1774 | { | |
bd9d6a3e | 1775 | genl_unregister_family(&nfsd_nl_family); |
6f6f84aa | 1776 | unregister_filesystem(&nfsd_fs_type); |
d76cc46b | 1777 | nfsd4_destroy_laundry_wq(); |
b10252c7 | 1778 | unregister_cld_notifier(); |
bd5ae928 | 1779 | unregister_pernet_subsys(&nfsd_net_ops); |
027690c7 | 1780 | nfsd_drc_slab_free(); |
1da177e4 LT |
1781 | remove_proc_entry("fs/nfs/exports", NULL); |
1782 | remove_proc_entry("fs/nfs", NULL); | |
1783 | nfsd_stat_shutdown(); | |
1784 | nfsd_lockd_shutdown(); | |
e8ff2a84 | 1785 | nfsd4_free_slabs(); |
9cf514cc | 1786 | nfsd4_exit_pnfs(); |
1da177e4 LT |
1787 | } |
1788 | ||
1789 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); | |
5865bafa | 1790 | MODULE_DESCRIPTION("In-kernel NFS server"); |
1da177e4 LT |
1791 | MODULE_LICENSE("GPL"); |
1792 | module_init(init_nfsd) | |
1793 | module_exit(exit_nfsd) |