]>
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 <[email protected]> | |
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> |
924f4fb0 | 18 | #include <linux/sunrpc/svc.h> |
143cb494 | 19 | #include <linux/module.h> |
e8a79fb1 | 20 | #include <linux/fsnotify.h> |
fa498386 | 21 | #include <linux/nfslocalio.h> |
1da177e4 | 22 | |
2ca72e17 | 23 | #include "idmap.h" |
9a74af21 BH |
24 | #include "nfsd.h" |
25 | #include "cache.h" | |
f3c7521f | 26 | #include "state.h" |
7ea34ac1 | 27 | #include "netns.h" |
9cf514cc | 28 | #include "pnfs.h" |
2e6c6e4c | 29 | #include "filecache.h" |
39d432fc | 30 | #include "trace.h" |
13727f85 | 31 | #include "netlink.h" |
9a74af21 | 32 | |
1da177e4 | 33 | /* |
b0b0c0a2 | 34 | * We have a single directory with several nodes in it. |
1da177e4 LT |
35 | */ |
36 | enum { | |
37 | NFSD_Root = 1, | |
1da177e4 | 38 | NFSD_List, |
20ad856e | 39 | NFSD_Export_Stats, |
e8e8753f | 40 | NFSD_Export_features, |
1da177e4 | 41 | NFSD_Fh, |
4373ea84 | 42 | NFSD_FO_UnlockIP, |
17efa372 | 43 | NFSD_FO_UnlockFS, |
1da177e4 | 44 | NFSD_Threads, |
eed2965a | 45 | NFSD_Pool_Threads, |
03cf6c9f | 46 | NFSD_Pool_Stats, |
a2f999a3 | 47 | NFSD_Reply_Cache_Stats, |
70c3b76c | 48 | NFSD_Versions, |
80212d59 | 49 | NFSD_Ports, |
596bbe53 | 50 | NFSD_MaxBlkSize, |
2e6c6e4c | 51 | NFSD_Filecache, |
1da177e4 | 52 | NFSD_Leasetime, |
efc4bb4f | 53 | NFSD_Gracetime, |
0964a3d3 | 54 | NFSD_RecoveryDir, |
7f5ef2e9 | 55 | NFSD_V4EndGrace, |
e8a79fb1 | 56 | NFSD_MaxReserved |
1da177e4 LT |
57 | }; |
58 | ||
59 | /* | |
60 | * write() for these nodes. | |
61 | */ | |
1da177e4 | 62 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size); |
b046ccdc CL |
63 | static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size); |
64 | static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size); | |
1da177e4 | 65 | static ssize_t write_threads(struct file *file, char *buf, size_t size); |
eed2965a | 66 | static ssize_t write_pool_threads(struct file *file, char *buf, size_t size); |
70c3b76c | 67 | static ssize_t write_versions(struct file *file, char *buf, size_t size); |
80212d59 | 68 | static ssize_t write_ports(struct file *file, char *buf, size_t size); |
596bbe53 | 69 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); |
70c3b76c | 70 | #ifdef CONFIG_NFSD_V4 |
1da177e4 | 71 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size); |
efc4bb4f | 72 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size); |
74fd4873 | 73 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
0964a3d3 | 74 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); |
74fd4873 | 75 | #endif |
7f5ef2e9 | 76 | static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size); |
70c3b76c | 77 | #endif |
1da177e4 | 78 | |
c2cdc2ab | 79 | static ssize_t (*const write_op[])(struct file *, char *, size_t) = { |
1da177e4 | 80 | [NFSD_Fh] = write_filehandle, |
b046ccdc CL |
81 | [NFSD_FO_UnlockIP] = write_unlock_ip, |
82 | [NFSD_FO_UnlockFS] = write_unlock_fs, | |
1da177e4 | 83 | [NFSD_Threads] = write_threads, |
eed2965a | 84 | [NFSD_Pool_Threads] = write_pool_threads, |
70c3b76c | 85 | [NFSD_Versions] = write_versions, |
80212d59 | 86 | [NFSD_Ports] = write_ports, |
596bbe53 | 87 | [NFSD_MaxBlkSize] = write_maxblksize, |
70c3b76c | 88 | #ifdef CONFIG_NFSD_V4 |
1da177e4 | 89 | [NFSD_Leasetime] = write_leasetime, |
efc4bb4f | 90 | [NFSD_Gracetime] = write_gracetime, |
74fd4873 | 91 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
0964a3d3 | 92 | [NFSD_RecoveryDir] = write_recoverydir, |
74fd4873 | 93 | #endif |
7f5ef2e9 | 94 | [NFSD_V4EndGrace] = write_v4_end_grace, |
70c3b76c | 95 | #endif |
1da177e4 LT |
96 | }; |
97 | ||
98 | static ssize_t nfsctl_transaction_write(struct file *file, const char __user *buf, size_t size, loff_t *pos) | |
99 | { | |
496ad9aa | 100 | ino_t ino = file_inode(file)->i_ino; |
1da177e4 LT |
101 | char *data; |
102 | ssize_t rv; | |
103 | ||
e8c96f8c | 104 | if (ino >= ARRAY_SIZE(write_op) || !write_op[ino]) |
1da177e4 LT |
105 | return -EINVAL; |
106 | ||
107 | data = simple_transaction_get(file, buf, size); | |
108 | if (IS_ERR(data)) | |
109 | return PTR_ERR(data); | |
110 | ||
3434d7aa CL |
111 | rv = write_op[ino](file, data, size); |
112 | if (rv < 0) | |
113 | return rv; | |
114 | ||
115 | simple_transaction_set(file, rv); | |
116 | return size; | |
1da177e4 LT |
117 | } |
118 | ||
7390022d N |
119 | static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos) |
120 | { | |
121 | if (! file->private_data) { | |
122 | /* An attempt to read a transaction file without writing | |
123 | * causes a 0-byte write so that the file can return | |
124 | * state information | |
125 | */ | |
126 | ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); | |
127 | if (rv < 0) | |
128 | return rv; | |
129 | } | |
130 | return simple_transaction_read(file, buf, size, pos); | |
131 | } | |
132 | ||
4b6f5d20 | 133 | static const struct file_operations transaction_ops = { |
1da177e4 | 134 | .write = nfsctl_transaction_write, |
7390022d | 135 | .read = nfsctl_transaction_read, |
1da177e4 | 136 | .release = simple_transaction_release, |
6038f373 | 137 | .llseek = default_llseek, |
1da177e4 LT |
138 | }; |
139 | ||
96d851c4 | 140 | static int exports_net_open(struct net *net, struct file *file) |
1da177e4 | 141 | { |
f2c7ea10 SK |
142 | int err; |
143 | struct seq_file *seq; | |
96d851c4 | 144 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
f2c7ea10 SK |
145 | |
146 | err = seq_open(file, &nfs_exports_op); | |
147 | if (err) | |
148 | return err; | |
149 | ||
150 | seq = file->private_data; | |
e5f06f72 | 151 | seq->private = nn->svc_export_cache; |
f2c7ea10 | 152 | return 0; |
1da177e4 LT |
153 | } |
154 | ||
96d851c4 SK |
155 | static int exports_nfsd_open(struct inode *inode, struct file *file) |
156 | { | |
157 | return exports_net_open(inode->i_sb->s_fs_info, file); | |
158 | } | |
159 | ||
160 | static const struct file_operations exports_nfsd_operations = { | |
161 | .open = exports_nfsd_open, | |
1da177e4 LT |
162 | .read = seq_read, |
163 | .llseek = seq_lseek, | |
164 | .release = seq_release, | |
165 | }; | |
166 | ||
e8e8753f BF |
167 | static int export_features_show(struct seq_file *m, void *v) |
168 | { | |
169 | seq_printf(m, "0x%x 0x%x\n", NFSEXP_ALLFLAGS, NFSEXP_SECINFO_FLAGS); | |
170 | return 0; | |
171 | } | |
172 | ||
9beeaab8 | 173 | DEFINE_SHOW_ATTRIBUTE(export_features); |
e8e8753f | 174 | |
c9f10f81 N |
175 | static int nfsd_pool_stats_open(struct inode *inode, struct file *file) |
176 | { | |
177 | struct nfsd_net *nn = net_generic(inode->i_sb->s_fs_info, nfsd_net_id); | |
178 | ||
179 | return svc_pool_stats_open(&nn->nfsd_info, file); | |
180 | } | |
181 | ||
828c0950 | 182 | static const struct file_operations pool_stats_operations = { |
03cf6c9f GB |
183 | .open = nfsd_pool_stats_open, |
184 | .read = seq_read, | |
185 | .llseek = seq_lseek, | |
7b207ccd | 186 | .release = seq_release, |
03cf6c9f GB |
187 | }; |
188 | ||
64776611 | 189 | DEFINE_SHOW_ATTRIBUTE(nfsd_reply_cache_stats); |
a2f999a3 | 190 | |
1342f9dd | 191 | DEFINE_SHOW_ATTRIBUTE(nfsd_file_cache_stats); |
2e6c6e4c | 192 | |
1da177e4 LT |
193 | /*----------------------------------------------------------------------------*/ |
194 | /* | |
195 | * payload - write methods | |
1da177e4 LT |
196 | */ |
197 | ||
244c7d44 AV |
198 | static inline struct net *netns(struct file *file) |
199 | { | |
200 | return file_inode(file)->i_sb->s_fs_info; | |
201 | } | |
1da177e4 | 202 | |
f2453978 | 203 | /* |
262a0982 CL |
204 | * write_unlock_ip - Release all locks used by a client |
205 | * | |
206 | * Experimental. | |
207 | * | |
208 | * Input: | |
209 | * buf: '\n'-terminated C string containing a | |
4116092b | 210 | * presentation format IP address |
262a0982 CL |
211 | * size: length of C string in @buf |
212 | * Output: | |
213 | * On success: returns zero if all specified locks were released; | |
214 | * returns one if one or more locks were not released | |
215 | * On error: return code is negative errno value | |
262a0982 | 216 | */ |
b046ccdc | 217 | static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size) |
4373ea84 | 218 | { |
4116092b CL |
219 | struct sockaddr_storage address; |
220 | struct sockaddr *sap = (struct sockaddr *)&address; | |
221 | size_t salen = sizeof(address); | |
367c8c7b | 222 | char *fo_path; |
244c7d44 | 223 | struct net *net = netns(file); |
4373ea84 WC |
224 | |
225 | /* sanity check */ | |
226 | if (size == 0) | |
227 | return -EINVAL; | |
228 | ||
229 | if (buf[size-1] != '\n') | |
230 | return -EINVAL; | |
231 | ||
232 | fo_path = buf; | |
233 | if (qword_get(&buf, fo_path, size) < 0) | |
234 | return -EINVAL; | |
235 | ||
11f77942 | 236 | if (rpc_pton(net, fo_path, size, sap, salen) == 0) |
4373ea84 | 237 | return -EINVAL; |
4373ea84 | 238 | |
39d432fc | 239 | trace_nfsd_ctl_unlock_ip(net, buf); |
4116092b | 240 | return nlmsvc_unlock_all_by_ip(sap); |
4373ea84 WC |
241 | } |
242 | ||
f2453978 | 243 | /* |
262a0982 CL |
244 | * write_unlock_fs - Release all locks on a local file system |
245 | * | |
246 | * Experimental. | |
247 | * | |
248 | * Input: | |
249 | * buf: '\n'-terminated C string containing the | |
250 | * absolute pathname of a local file system | |
251 | * size: length of C string in @buf | |
252 | * Output: | |
253 | * On success: returns zero if all specified locks were released; | |
254 | * returns one if one or more locks were not released | |
255 | * On error: return code is negative errno value | |
256 | */ | |
b046ccdc | 257 | static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size) |
17efa372 | 258 | { |
a63bb996 | 259 | struct path path; |
17efa372 WC |
260 | char *fo_path; |
261 | int error; | |
262 | ||
263 | /* sanity check */ | |
264 | if (size == 0) | |
265 | return -EINVAL; | |
266 | ||
267 | if (buf[size-1] != '\n') | |
268 | return -EINVAL; | |
269 | ||
270 | fo_path = buf; | |
271 | if (qword_get(&buf, fo_path, size) < 0) | |
272 | return -EINVAL; | |
39d432fc | 273 | trace_nfsd_ctl_unlock_fs(netns(file), fo_path); |
a63bb996 | 274 | error = kern_path(fo_path, 0, &path); |
17efa372 WC |
275 | if (error) |
276 | return error; | |
277 | ||
262a0982 CL |
278 | /* |
279 | * XXX: Needs better sanity checking. Otherwise we could end up | |
280 | * releasing locks on the wrong file system. | |
281 | * | |
282 | * For example: | |
283 | * 1. Does the path refer to a directory? | |
284 | * 2. Is that directory a mount point, or | |
285 | * 3. Is that directory the root of an exported file system? | |
286 | */ | |
d8c9584e | 287 | error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb); |
1ac3629b | 288 | nfsd4_revoke_states(netns(file), path.dentry->d_sb); |
17efa372 | 289 | |
a63bb996 | 290 | path_put(&path); |
17efa372 WC |
291 | return error; |
292 | } | |
293 | ||
f2453978 | 294 | /* |
262a0982 CL |
295 | * write_filehandle - Get a variable-length NFS file handle by path |
296 | * | |
297 | * On input, the buffer contains a '\n'-terminated C string comprised of | |
298 | * three alphanumeric words separated by whitespace. The string may | |
299 | * contain escape sequences. | |
300 | * | |
301 | * Input: | |
302 | * buf: | |
303 | * domain: client domain name | |
304 | * path: export pathname | |
305 | * maxsize: numeric maximum size of | |
306 | * @buf | |
307 | * size: length of C string in @buf | |
308 | * Output: | |
309 | * On success: passed-in buffer filled with '\n'-terminated C | |
310 | * string containing a ASCII hex text version | |
311 | * of the NFS file handle; | |
312 | * return code is the size in bytes of the string | |
313 | * On error: return code is negative errno value | |
314 | */ | |
1da177e4 LT |
315 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size) |
316 | { | |
1da177e4 | 317 | char *dname, *path; |
3f649ab7 | 318 | int maxsize; |
1da177e4 LT |
319 | char *mesg = buf; |
320 | int len; | |
321 | struct auth_domain *dom; | |
322 | struct knfsd_fh fh; | |
323 | ||
87d26ea7 BF |
324 | if (size == 0) |
325 | return -EINVAL; | |
326 | ||
1da177e4 LT |
327 | if (buf[size-1] != '\n') |
328 | return -EINVAL; | |
329 | buf[size-1] = 0; | |
330 | ||
331 | dname = mesg; | |
332 | len = qword_get(&mesg, dname, size); | |
54224f04 CL |
333 | if (len <= 0) |
334 | return -EINVAL; | |
442a6290 | 335 | |
1da177e4 LT |
336 | path = dname+len+1; |
337 | len = qword_get(&mesg, path, size); | |
54224f04 CL |
338 | if (len <= 0) |
339 | return -EINVAL; | |
1da177e4 LT |
340 | |
341 | len = get_int(&mesg, &maxsize); | |
342 | if (len) | |
343 | return len; | |
344 | ||
345 | if (maxsize < NFS_FHSIZE) | |
346 | return -EINVAL; | |
3c7aa15d | 347 | maxsize = min(maxsize, NFS3_FHSIZE); |
1da177e4 | 348 | |
442a6290 | 349 | if (qword_get(&mesg, mesg, size) > 0) |
1da177e4 LT |
350 | return -EINVAL; |
351 | ||
39d432fc CL |
352 | trace_nfsd_ctl_filehandle(netns(file), dname, path, maxsize); |
353 | ||
1da177e4 LT |
354 | /* we have all the words, they are in buf.. */ |
355 | dom = unix_domain_find(dname); | |
356 | if (!dom) | |
357 | return -ENOMEM; | |
358 | ||
442a6290 | 359 | len = exp_rootfh(netns(file), dom, path, &fh, maxsize); |
1da177e4 LT |
360 | auth_domain_put(dom); |
361 | if (len) | |
362 | return len; | |
d8b26071 | 363 | |
54224f04 CL |
364 | mesg = buf; |
365 | len = SIMPLE_TRANSACTION_LIMIT; | |
d8b26071 | 366 | qword_addhex(&mesg, &len, fh.fh_raw, fh.fh_size); |
1da177e4 | 367 | mesg[-1] = '\n'; |
d8b26071 | 368 | return mesg - buf; |
1da177e4 LT |
369 | } |
370 | ||
f2453978 | 371 | /* |
262a0982 CL |
372 | * write_threads - Start NFSD, or report the current number of running threads |
373 | * | |
374 | * Input: | |
375 | * buf: ignored | |
376 | * size: zero | |
377 | * Output: | |
378 | * On success: passed-in buffer filled with '\n'-terminated C | |
379 | * string numeric value representing the number of | |
380 | * running NFSD threads; | |
381 | * return code is the size in bytes of the string | |
382 | * On error: return code is zero | |
383 | * | |
384 | * OR | |
385 | * | |
386 | * Input: | |
387 | * buf: C string containing an unsigned | |
388 | * integer value representing the | |
389 | * number of NFSD threads to start | |
390 | * size: non-zero length of C string in @buf | |
391 | * Output: | |
392 | * On success: NFS service is started; | |
393 | * passed-in buffer filled with '\n'-terminated C | |
394 | * string numeric value representing the number of | |
395 | * running NFSD threads; | |
396 | * return code is the size in bytes of the string | |
397 | * On error: return code is zero or a negative errno value | |
398 | */ | |
1da177e4 LT |
399 | static ssize_t write_threads(struct file *file, char *buf, size_t size) |
400 | { | |
1da177e4 LT |
401 | char *mesg = buf; |
402 | int rv; | |
244c7d44 | 403 | struct net *net = netns(file); |
d41a9417 | 404 | |
1da177e4 LT |
405 | if (size > 0) { |
406 | int newthreads; | |
407 | rv = get_int(&mesg, &newthreads); | |
408 | if (rv) | |
409 | return rv; | |
9e074856 | 410 | if (newthreads < 0) |
1da177e4 | 411 | return -EINVAL; |
39d432fc | 412 | trace_nfsd_ctl_threads(net, newthreads); |
0842b4c8 | 413 | mutex_lock(&nfsd_mutex); |
b4d8f228 | 414 | rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL); |
0842b4c8 | 415 | mutex_unlock(&nfsd_mutex); |
82e12fe9 | 416 | if (rv < 0) |
1da177e4 | 417 | return rv; |
82e12fe9 | 418 | } else |
9dd9845f | 419 | rv = nfsd_nrthreads(net); |
e06b6405 | 420 | |
82e12fe9 | 421 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); |
1da177e4 LT |
422 | } |
423 | ||
f2453978 | 424 | /* |
262a0982 CL |
425 | * write_pool_threads - Set or report the current number of threads per pool |
426 | * | |
427 | * Input: | |
428 | * buf: ignored | |
429 | * size: zero | |
430 | * | |
431 | * OR | |
432 | * | |
433 | * Input: | |
442a6290 CL |
434 | * buf: C string containing whitespace- |
435 | * separated unsigned integer values | |
262a0982 CL |
436 | * representing the number of NFSD |
437 | * threads to start in each pool | |
438 | * size: non-zero length of C string in @buf | |
439 | * Output: | |
440 | * On success: passed-in buffer filled with '\n'-terminated C | |
441 | * string containing integer values representing the | |
442 | * number of NFSD threads in each pool; | |
443 | * return code is the size in bytes of the string | |
444 | * On error: return code is zero or a negative errno value | |
445 | */ | |
eed2965a GB |
446 | static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) |
447 | { | |
448 | /* if size > 0, look for an array of number of threads per node | |
449 | * and apply them then write out number of threads per node as reply | |
450 | */ | |
451 | char *mesg = buf; | |
452 | int i; | |
453 | int rv; | |
454 | int len; | |
bedbdd8b | 455 | int npools; |
eed2965a | 456 | int *nthreads; |
244c7d44 | 457 | struct net *net = netns(file); |
eed2965a | 458 | |
bedbdd8b | 459 | mutex_lock(&nfsd_mutex); |
9dd9845f | 460 | npools = nfsd_nrpools(net); |
eed2965a GB |
461 | if (npools == 0) { |
462 | /* | |
463 | * NFS is shut down. The admin can start it by | |
464 | * writing to the threads file but NOT the pool_threads | |
465 | * file, sorry. Report zero threads. | |
466 | */ | |
bedbdd8b | 467 | mutex_unlock(&nfsd_mutex); |
eed2965a GB |
468 | strcpy(buf, "0\n"); |
469 | return strlen(buf); | |
470 | } | |
471 | ||
472 | nthreads = kcalloc(npools, sizeof(int), GFP_KERNEL); | |
bedbdd8b | 473 | rv = -ENOMEM; |
eed2965a | 474 | if (nthreads == NULL) |
bedbdd8b | 475 | goto out_free; |
eed2965a GB |
476 | |
477 | if (size > 0) { | |
478 | for (i = 0; i < npools; i++) { | |
479 | rv = get_int(&mesg, &nthreads[i]); | |
480 | if (rv == -ENOENT) | |
481 | break; /* fewer numbers than pools */ | |
482 | if (rv) | |
483 | goto out_free; /* syntax error */ | |
484 | rv = -EINVAL; | |
485 | if (nthreads[i] < 0) | |
486 | goto out_free; | |
39d432fc | 487 | trace_nfsd_ctl_pool_threads(net, i, nthreads[i]); |
eed2965a | 488 | } |
b4d8f228 JL |
489 | |
490 | /* | |
491 | * There must always be a thread in pool 0; the admin | |
492 | * can't shut down NFS completely using pool_threads. | |
493 | */ | |
494 | if (nthreads[0] == 0) | |
495 | nthreads[0] = 1; | |
496 | ||
3938a0d5 | 497 | rv = nfsd_set_nrthreads(i, nthreads, net); |
eed2965a GB |
498 | if (rv) |
499 | goto out_free; | |
500 | } | |
501 | ||
9dd9845f | 502 | rv = nfsd_get_nrthreads(npools, nthreads, net); |
eed2965a GB |
503 | if (rv) |
504 | goto out_free; | |
505 | ||
506 | mesg = buf; | |
507 | size = SIMPLE_TRANSACTION_LIMIT; | |
508 | for (i = 0; i < npools && size > 0; i++) { | |
509 | snprintf(mesg, size, "%d%c", nthreads[i], (i == npools-1 ? '\n' : ' ')); | |
510 | len = strlen(mesg); | |
511 | size -= len; | |
512 | mesg += len; | |
513 | } | |
413d63d7 | 514 | rv = mesg - buf; |
eed2965a GB |
515 | out_free: |
516 | kfree(nthreads); | |
bedbdd8b | 517 | mutex_unlock(&nfsd_mutex); |
eed2965a GB |
518 | return rv; |
519 | } | |
520 | ||
ff7d1179 | 521 | static ssize_t |
e333f3bb TM |
522 | nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining, |
523 | const char *sep, unsigned vers, int minor) | |
ff7d1179 | 524 | { |
abcb4dac | 525 | const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; |
e333f3bb | 526 | bool supported = !!nfsd_vers(nn, vers, NFSD_TEST); |
ff7d1179 | 527 | |
abcb4dac | 528 | if (vers == 4 && minor >= 0 && |
e333f3bb | 529 | !nfsd_minorversion(nn, minor, NFSD_TEST)) |
ff7d1179 | 530 | supported = false; |
abcb4dac N |
531 | if (minor == 0 && supported) |
532 | /* | |
533 | * special case for backward compatability. | |
534 | * +4.0 is never reported, it is implied by | |
535 | * +4, unless -4.0 is present. | |
536 | */ | |
537 | return 0; | |
ff7d1179 TM |
538 | return snprintf(buf, remaining, format, sep, |
539 | supported ? '+' : '-', vers, minor); | |
540 | } | |
541 | ||
3dd98a3b | 542 | static ssize_t __write_versions(struct file *file, char *buf, size_t size) |
70c3b76c | 543 | { |
70c3b76c | 544 | char *mesg = buf; |
8daf220a | 545 | char *vers, *minorp, sign; |
261758b5 | 546 | int len, num, remaining; |
70c3b76c N |
547 | ssize_t tlen = 0; |
548 | char *sep; | |
244c7d44 | 549 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
70c3b76c | 550 | |
442a6290 | 551 | if (size > 0) { |
9dd9845f | 552 | if (nn->nfsd_serv) |
6658d3a7 | 553 | /* Cannot change versions without updating |
9dd9845f | 554 | * nn->nfsd_serv->sv_xdrsize, and reallocing |
6658d3a7 N |
555 | * rq_argp and rq_resp |
556 | */ | |
70c3b76c N |
557 | return -EBUSY; |
558 | if (buf[size-1] != '\n') | |
559 | return -EINVAL; | |
560 | buf[size-1] = 0; | |
39d432fc | 561 | trace_nfsd_ctl_version(netns(file), buf); |
70c3b76c N |
562 | |
563 | vers = mesg; | |
564 | len = qword_get(&mesg, vers, size); | |
565 | if (len <= 0) return -EINVAL; | |
566 | do { | |
d3635ff0 | 567 | enum vers_op cmd; |
abcb4dac | 568 | unsigned minor; |
70c3b76c N |
569 | sign = *vers; |
570 | if (sign == '+' || sign == '-') | |
8daf220a | 571 | num = simple_strtol((vers+1), &minorp, 0); |
70c3b76c | 572 | else |
8daf220a BH |
573 | num = simple_strtol(vers, &minorp, 0); |
574 | if (*minorp == '.') { | |
ff89be87 | 575 | if (num != 4) |
8daf220a | 576 | return -EINVAL; |
e35659f1 | 577 | if (kstrtouint(minorp+1, 0, &minor) < 0) |
8daf220a | 578 | return -EINVAL; |
abcb4dac N |
579 | } |
580 | ||
d3635ff0 | 581 | cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; |
70c3b76c | 582 | switch(num) { |
2f3a4b2a | 583 | #ifdef CONFIG_NFSD_V2 |
70c3b76c | 584 | case 2: |
2f3a4b2a | 585 | #endif |
70c3b76c | 586 | case 3: |
e333f3bb | 587 | nfsd_vers(nn, num, cmd); |
70c3b76c | 588 | break; |
d3635ff0 | 589 | case 4: |
abcb4dac | 590 | if (*minorp == '.') { |
e333f3bb | 591 | if (nfsd_minorversion(nn, minor, cmd) < 0) |
abcb4dac | 592 | return -EINVAL; |
e333f3bb | 593 | } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { |
abcb4dac N |
594 | /* |
595 | * Either we have +4 and no minors are enabled, | |
596 | * or we have -4 and at least one minor is enabled. | |
597 | * In either case, propagate 'cmd' to all minors. | |
598 | */ | |
599 | minor = 0; | |
e333f3bb | 600 | while (nfsd_minorversion(nn, minor, cmd) >= 0) |
abcb4dac N |
601 | minor++; |
602 | } | |
603 | break; | |
70c3b76c | 604 | default: |
8e823baf JL |
605 | /* Ignore requests to disable non-existent versions */ |
606 | if (cmd == NFSD_SET) | |
607 | return -EINVAL; | |
70c3b76c N |
608 | } |
609 | vers += len + 1; | |
70c3b76c N |
610 | } while ((len = qword_get(&mesg, vers, size)) > 0); |
611 | /* If all get turned off, turn them back on, as | |
612 | * having no versions is BAD | |
613 | */ | |
e333f3bb | 614 | nfsd_reset_versions(nn); |
70c3b76c | 615 | } |
261758b5 | 616 | |
70c3b76c | 617 | /* Now write current state into reply buffer */ |
70c3b76c | 618 | sep = ""; |
261758b5 | 619 | remaining = SIMPLE_TRANSACTION_LIMIT; |
ff7d1179 | 620 | for (num=2 ; num <= 4 ; num++) { |
abcb4dac | 621 | int minor; |
e333f3bb | 622 | if (!nfsd_vers(nn, num, NFSD_AVAIL)) |
ff7d1179 | 623 | continue; |
abcb4dac N |
624 | |
625 | minor = -1; | |
ff7d1179 | 626 | do { |
e333f3bb | 627 | len = nfsd_print_version_support(nn, buf, remaining, |
ff7d1179 | 628 | sep, num, minor); |
818f2f57 | 629 | if (len >= remaining) |
ff7d1179 | 630 | goto out; |
261758b5 CL |
631 | remaining -= len; |
632 | buf += len; | |
633 | tlen += len; | |
ff7d1179 | 634 | minor++; |
abcb4dac N |
635 | if (len) |
636 | sep = " "; | |
ff7d1179 TM |
637 | } while (num == 4 && minor <= NFSD_SUPPORTED_MINOR_VERSION); |
638 | } | |
639 | out: | |
261758b5 | 640 | len = snprintf(buf, remaining, "\n"); |
818f2f57 | 641 | if (len >= remaining) |
261758b5 CL |
642 | return -EINVAL; |
643 | return tlen + len; | |
70c3b76c N |
644 | } |
645 | ||
f2453978 | 646 | /* |
262a0982 CL |
647 | * write_versions - Set or report the available NFS protocol versions |
648 | * | |
649 | * Input: | |
650 | * buf: ignored | |
651 | * size: zero | |
652 | * Output: | |
653 | * On success: passed-in buffer filled with '\n'-terminated C | |
654 | * string containing positive or negative integer | |
655 | * values representing the current status of each | |
656 | * protocol version; | |
657 | * return code is the size in bytes of the string | |
658 | * On error: return code is zero or a negative errno value | |
659 | * | |
660 | * OR | |
661 | * | |
662 | * Input: | |
442a6290 CL |
663 | * buf: C string containing whitespace- |
664 | * separated positive or negative | |
665 | * integer values representing NFS | |
666 | * protocol versions to enable ("+n") | |
667 | * or disable ("-n") | |
262a0982 CL |
668 | * size: non-zero length of C string in @buf |
669 | * Output: | |
670 | * On success: status of zero or more protocol versions has | |
671 | * been updated; passed-in buffer filled with | |
672 | * '\n'-terminated C string containing positive | |
673 | * or negative integer values representing the | |
674 | * current status of each protocol version; | |
675 | * return code is the size in bytes of the string | |
676 | * On error: return code is zero or a negative errno value | |
677 | */ | |
3dd98a3b JL |
678 | static ssize_t write_versions(struct file *file, char *buf, size_t size) |
679 | { | |
680 | ssize_t rv; | |
681 | ||
682 | mutex_lock(&nfsd_mutex); | |
683 | rv = __write_versions(file, buf, size); | |
684 | mutex_unlock(&nfsd_mutex); | |
685 | return rv; | |
686 | } | |
687 | ||
0a5372d8 CL |
688 | /* |
689 | * Zero-length write. Return a list of NFSD's current listener | |
690 | * transports. | |
691 | */ | |
9dd9845f | 692 | static ssize_t __write_ports_names(char *buf, struct net *net) |
0a5372d8 | 693 | { |
9dd9845f SK |
694 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
695 | ||
696 | if (nn->nfsd_serv == NULL) | |
0a5372d8 | 697 | return 0; |
9dd9845f | 698 | return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); |
0a5372d8 CL |
699 | } |
700 | ||
0b7c2f6f CL |
701 | /* |
702 | * A single 'fd' number was written, in which case it must be for | |
703 | * a socket of a supported family/protocol, and we use it as an | |
704 | * nfsd listener. | |
705 | */ | |
4df493a2 | 706 | static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred) |
0b7c2f6f CL |
707 | { |
708 | char *mesg = buf; | |
709 | int fd, err; | |
9dd9845f | 710 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
64e63041 | 711 | struct svc_serv *serv; |
0b7c2f6f CL |
712 | |
713 | err = get_int(&mesg, &fd); | |
714 | if (err != 0 || fd < 0) | |
715 | return -EINVAL; | |
39d432fc | 716 | trace_nfsd_ctl_ports_addfd(net, fd); |
0b7c2f6f | 717 | |
6777436b | 718 | err = nfsd_create_serv(net); |
0b7c2f6f CL |
719 | if (err != 0) |
720 | return err; | |
721 | ||
64e63041 JL |
722 | serv = nn->nfsd_serv; |
723 | err = svc_addsock(serv, net, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); | |
0b7c2f6f | 724 | |
1e3577a4 | 725 | if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) |
17419aef N |
726 | nfsd_destroy_serv(net); |
727 | ||
ea068bad | 728 | return err; |
0b7c2f6f CL |
729 | } |
730 | ||
4eb68c26 | 731 | /* |
442a6290 | 732 | * A transport listener is added by writing its transport name and |
4eb68c26 CL |
733 | * a port number. |
734 | */ | |
4df493a2 | 735 | static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred) |
4eb68c26 CL |
736 | { |
737 | char transport[16]; | |
37498292 | 738 | struct svc_xprt *xprt; |
4eb68c26 | 739 | int port, err; |
9dd9845f | 740 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
64e63041 | 741 | struct svc_serv *serv; |
4eb68c26 | 742 | |
a10fded1 | 743 | if (sscanf(buf, "%15s %5u", transport, &port) != 2) |
4eb68c26 CL |
744 | return -EINVAL; |
745 | ||
4be929be | 746 | if (port < 1 || port > USHRT_MAX) |
4eb68c26 | 747 | return -EINVAL; |
39d432fc | 748 | trace_nfsd_ctl_ports_addxprt(net, transport, port); |
4eb68c26 | 749 | |
6777436b | 750 | err = nfsd_create_serv(net); |
4eb68c26 CL |
751 | if (err != 0) |
752 | return err; | |
753 | ||
64e63041 JL |
754 | serv = nn->nfsd_serv; |
755 | err = svc_xprt_create(serv, transport, net, | |
352ad314 | 756 | PF_INET, port, SVC_SOCK_ANONYMOUS, cred); |
68717908 | 757 | if (err < 0) |
37498292 CL |
758 | goto out_err; |
759 | ||
64e63041 | 760 | err = svc_xprt_create(serv, transport, net, |
352ad314 | 761 | PF_INET6, port, SVC_SOCK_ANONYMOUS, cred); |
37498292 CL |
762 | if (err < 0 && err != -EAFNOSUPPORT) |
763 | goto out_close; | |
0cd14a06 | 764 | |
4eb68c26 | 765 | return 0; |
37498292 | 766 | out_close: |
64e63041 | 767 | xprt = svc_find_xprt(serv, transport, net, PF_INET, port); |
37498292 | 768 | if (xprt != NULL) { |
4355d767 | 769 | svc_xprt_close(xprt); |
37498292 CL |
770 | svc_xprt_put(xprt); |
771 | } | |
772 | out_err: | |
1e3577a4 | 773 | if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) |
17419aef | 774 | nfsd_destroy_serv(net); |
2a501f55 | 775 | |
37498292 | 776 | return err; |
4eb68c26 CL |
777 | } |
778 | ||
08160352 SK |
779 | static ssize_t __write_ports(struct file *file, char *buf, size_t size, |
780 | struct net *net) | |
80212d59 | 781 | { |
0a5372d8 | 782 | if (size == 0) |
9dd9845f | 783 | return __write_ports_names(buf, net); |
0b7c2f6f CL |
784 | |
785 | if (isdigit(buf[0])) | |
4df493a2 | 786 | return __write_ports_addfd(buf, net, file->f_cred); |
82d56591 | 787 | |
4eb68c26 | 788 | if (isalpha(buf[0])) |
4df493a2 | 789 | return __write_ports_addxprt(buf, net, file->f_cred); |
4cd5dc75 | 790 | |
b41b66d6 | 791 | return -EINVAL; |
80212d59 N |
792 | } |
793 | ||
f2453978 | 794 | /* |
262a0982 CL |
795 | * write_ports - Pass a socket file descriptor or transport name to listen on |
796 | * | |
797 | * Input: | |
798 | * buf: ignored | |
799 | * size: zero | |
800 | * Output: | |
801 | * On success: passed-in buffer filled with a '\n'-terminated C | |
802 | * string containing a whitespace-separated list of | |
803 | * named NFSD listeners; | |
804 | * return code is the size in bytes of the string | |
805 | * On error: return code is zero or a negative errno value | |
806 | * | |
807 | * OR | |
808 | * | |
809 | * Input: | |
810 | * buf: C string containing an unsigned | |
811 | * integer value representing a bound | |
812 | * but unconnected socket that is to be | |
c71206a7 CL |
813 | * used as an NFSD listener; listen(3) |
814 | * must be called for a SOCK_STREAM | |
815 | * socket, otherwise it is ignored | |
262a0982 CL |
816 | * size: non-zero length of C string in @buf |
817 | * Output: | |
818 | * On success: NFS service is started; | |
819 | * passed-in buffer filled with a '\n'-terminated C | |
820 | * string containing a unique alphanumeric name of | |
821 | * the listener; | |
822 | * return code is the size in bytes of the string | |
823 | * On error: return code is a negative errno value | |
824 | * | |
825 | * OR | |
826 | * | |
827 | * Input: | |
262a0982 CL |
828 | * buf: C string containing a transport |
829 | * name and an unsigned integer value | |
830 | * representing the port to listen on, | |
831 | * separated by whitespace | |
832 | * size: non-zero length of C string in @buf | |
833 | * Output: | |
834 | * On success: returns zero; NFS service is started | |
835 | * On error: return code is a negative errno value | |
262a0982 | 836 | */ |
bedbdd8b NB |
837 | static ssize_t write_ports(struct file *file, char *buf, size_t size) |
838 | { | |
839 | ssize_t rv; | |
3dd98a3b | 840 | |
bedbdd8b | 841 | mutex_lock(&nfsd_mutex); |
244c7d44 | 842 | rv = __write_ports(file, buf, size, netns(file)); |
bedbdd8b NB |
843 | mutex_unlock(&nfsd_mutex); |
844 | return rv; | |
845 | } | |
846 | ||
847 | ||
596bbe53 N |
848 | int nfsd_max_blksize; |
849 | ||
f2453978 | 850 | /* |
262a0982 CL |
851 | * write_maxblksize - Set or report the current NFS blksize |
852 | * | |
853 | * Input: | |
854 | * buf: ignored | |
855 | * size: zero | |
856 | * | |
857 | * OR | |
858 | * | |
859 | * Input: | |
442a6290 CL |
860 | * buf: C string containing an unsigned |
861 | * integer value representing the new | |
862 | * NFS blksize | |
262a0982 CL |
863 | * size: non-zero length of C string in @buf |
864 | * Output: | |
865 | * On success: passed-in buffer filled with '\n'-terminated C string | |
866 | * containing numeric value of the current NFS blksize | |
867 | * setting; | |
868 | * return code is the size in bytes of the string | |
869 | * On error: return code is zero or a negative errno value | |
870 | */ | |
596bbe53 N |
871 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) |
872 | { | |
873 | char *mesg = buf; | |
244c7d44 | 874 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 875 | |
596bbe53 N |
876 | if (size > 0) { |
877 | int bsize; | |
878 | int rv = get_int(&mesg, &bsize); | |
879 | if (rv) | |
880 | return rv; | |
39d432fc CL |
881 | trace_nfsd_ctl_maxblksize(netns(file), bsize); |
882 | ||
596bbe53 N |
883 | /* force bsize into allowed range and |
884 | * required alignment. | |
885 | */ | |
3c7aa15d KM |
886 | bsize = max_t(int, bsize, 1024); |
887 | bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE); | |
596bbe53 | 888 | bsize &= ~(1024-1); |
bedbdd8b | 889 | mutex_lock(&nfsd_mutex); |
9dd9845f | 890 | if (nn->nfsd_serv) { |
bedbdd8b | 891 | mutex_unlock(&nfsd_mutex); |
596bbe53 N |
892 | return -EBUSY; |
893 | } | |
894 | nfsd_max_blksize = bsize; | |
bedbdd8b | 895 | mutex_unlock(&nfsd_mutex); |
596bbe53 | 896 | } |
e06b6405 CL |
897 | |
898 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", | |
899 | nfsd_max_blksize); | |
596bbe53 N |
900 | } |
901 | ||
70c3b76c | 902 | #ifdef CONFIG_NFSD_V4 |
9dd9845f | 903 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, |
20b7d86f | 904 | time64_t *time, struct nfsd_net *nn) |
1da177e4 | 905 | { |
39d432fc | 906 | struct dentry *dentry = file_dentry(file); |
1da177e4 | 907 | char *mesg = buf; |
f0135740 | 908 | int rv, i; |
1da177e4 LT |
909 | |
910 | if (size > 0) { | |
9dd9845f | 911 | if (nn->nfsd_serv) |
3dd98a3b | 912 | return -EBUSY; |
f0135740 | 913 | rv = get_int(&mesg, &i); |
1da177e4 LT |
914 | if (rv) |
915 | return rv; | |
39d432fc CL |
916 | trace_nfsd_ctl_time(netns(file), dentry->d_name.name, |
917 | dentry->d_name.len, i); | |
918 | ||
e7b184f1 BF |
919 | /* |
920 | * Some sanity checking. We don't have a reason for | |
921 | * these particular numbers, but problems with the | |
922 | * extremes are: | |
923 | * - Too short: the briefest network outage may | |
924 | * cause clients to lose all their locks. Also, | |
925 | * the frequent polling may be wasteful. | |
926 | * - Too long: do you really want reboot recovery | |
927 | * to take more than an hour? Or to make other | |
928 | * clients wait an hour before being able to | |
929 | * revoke a dead client's locks? | |
930 | */ | |
f0135740 | 931 | if (i < 10 || i > 3600) |
1da177e4 | 932 | return -EINVAL; |
f0135740 | 933 | *time = i; |
1da177e4 | 934 | } |
e06b6405 | 935 | |
20b7d86f | 936 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time); |
f0135740 BF |
937 | } |
938 | ||
9dd9845f | 939 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, |
20b7d86f | 940 | time64_t *time, struct nfsd_net *nn) |
f0135740 BF |
941 | { |
942 | ssize_t rv; | |
943 | ||
944 | mutex_lock(&nfsd_mutex); | |
9dd9845f | 945 | rv = __nfsd4_write_time(file, buf, size, time, nn); |
f0135740 BF |
946 | mutex_unlock(&nfsd_mutex); |
947 | return rv; | |
1da177e4 LT |
948 | } |
949 | ||
f2453978 | 950 | /* |
262a0982 CL |
951 | * write_leasetime - Set or report the current NFSv4 lease time |
952 | * | |
953 | * Input: | |
954 | * buf: ignored | |
955 | * size: zero | |
956 | * | |
957 | * OR | |
958 | * | |
959 | * Input: | |
960 | * buf: C string containing an unsigned | |
961 | * integer value representing the new | |
962 | * NFSv4 lease expiry time | |
963 | * size: non-zero length of C string in @buf | |
964 | * Output: | |
965 | * On success: passed-in buffer filled with '\n'-terminated C | |
966 | * string containing unsigned integer value of the | |
967 | * current lease expiry time; | |
968 | * return code is the size in bytes of the string | |
969 | * On error: return code is zero or a negative errno value | |
970 | */ | |
3dd98a3b JL |
971 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) |
972 | { | |
244c7d44 | 973 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 974 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); |
3dd98a3b JL |
975 | } |
976 | ||
f2453978 | 977 | /* |
efc4bb4f BF |
978 | * write_gracetime - Set or report current NFSv4 grace period time |
979 | * | |
980 | * As above, but sets the time of the NFSv4 grace period. | |
981 | * | |
982 | * Note this should never be set to less than the *previous* | |
983 | * lease-period time, but we don't try to enforce this. (In the common | |
984 | * case (a new boot), we don't know what the previous lease time was | |
985 | * anyway.) | |
986 | */ | |
987 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size) | |
988 | { | |
244c7d44 | 989 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
9dd9845f | 990 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); |
efc4bb4f BF |
991 | } |
992 | ||
74fd4873 | 993 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
9dd9845f SK |
994 | static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, |
995 | struct nfsd_net *nn) | |
0964a3d3 N |
996 | { |
997 | char *mesg = buf; | |
998 | char *recdir; | |
999 | int len, status; | |
1000 | ||
3dd98a3b | 1001 | if (size > 0) { |
9dd9845f | 1002 | if (nn->nfsd_serv) |
3dd98a3b JL |
1003 | return -EBUSY; |
1004 | if (size > PATH_MAX || buf[size-1] != '\n') | |
1005 | return -EINVAL; | |
1006 | buf[size-1] = 0; | |
0964a3d3 | 1007 | |
3dd98a3b JL |
1008 | recdir = mesg; |
1009 | len = qword_get(&mesg, recdir, size); | |
1010 | if (len <= 0) | |
1011 | return -EINVAL; | |
39d432fc | 1012 | trace_nfsd_ctl_recoverydir(netns(file), recdir); |
0964a3d3 | 1013 | |
3dd98a3b | 1014 | status = nfs4_reset_recoverydir(recdir); |
69049961 AK |
1015 | if (status) |
1016 | return status; | |
3dd98a3b | 1017 | } |
3d72ab8f CL |
1018 | |
1019 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n", | |
1020 | nfs4_recoverydir()); | |
0964a3d3 | 1021 | } |
3dd98a3b | 1022 | |
f2453978 | 1023 | /* |
262a0982 CL |
1024 | * write_recoverydir - Set or report the pathname of the recovery directory |
1025 | * | |
1026 | * Input: | |
1027 | * buf: ignored | |
1028 | * size: zero | |
1029 | * | |
1030 | * OR | |
1031 | * | |
1032 | * Input: | |
1033 | * buf: C string containing the pathname | |
1034 | * of the directory on a local file | |
1035 | * system containing permanent NFSv4 | |
1036 | * recovery data | |
1037 | * size: non-zero length of C string in @buf | |
1038 | * Output: | |
1039 | * On success: passed-in buffer filled with '\n'-terminated C string | |
1040 | * containing the current recovery pathname setting; | |
1041 | * return code is the size in bytes of the string | |
1042 | * On error: return code is zero or a negative errno value | |
1043 | */ | |
3dd98a3b JL |
1044 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) |
1045 | { | |
1046 | ssize_t rv; | |
244c7d44 | 1047 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
3dd98a3b JL |
1048 | |
1049 | mutex_lock(&nfsd_mutex); | |
9dd9845f | 1050 | rv = __write_recoverydir(file, buf, size, nn); |
3dd98a3b JL |
1051 | mutex_unlock(&nfsd_mutex); |
1052 | return rv; | |
1053 | } | |
74fd4873 | 1054 | #endif |
3dd98a3b | 1055 | |
f2453978 | 1056 | /* |
7f5ef2e9 JL |
1057 | * write_v4_end_grace - release grace period for nfsd's v4.x lock manager |
1058 | * | |
1059 | * Input: | |
1060 | * buf: ignored | |
1061 | * size: zero | |
1062 | * OR | |
1063 | * | |
1064 | * Input: | |
442a6290 | 1065 | * buf: any value |
7f5ef2e9 JL |
1066 | * size: non-zero length of C string in @buf |
1067 | * Output: | |
1068 | * passed-in buffer filled with "Y" or "N" with a newline | |
1069 | * and NULL-terminated C string. This indicates whether | |
1070 | * the grace period has ended in the current net | |
1071 | * namespace. Return code is the size in bytes of the | |
1072 | * string. Writing a string that starts with 'Y', 'y', or | |
1073 | * '1' to the file will end the grace period for nfsd's v4 | |
1074 | * lock manager. | |
1075 | */ | |
1076 | static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size) | |
1077 | { | |
244c7d44 | 1078 | struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); |
7f5ef2e9 JL |
1079 | |
1080 | if (size > 0) { | |
1081 | switch(buf[0]) { | |
1082 | case 'Y': | |
1083 | case 'y': | |
1084 | case '1': | |
dd838821 | 1085 | if (!nn->nfsd_serv) |
62a063b8 | 1086 | return -EBUSY; |
39d432fc | 1087 | trace_nfsd_end_grace(netns(file)); |
8073a98e | 1088 | nfsd4_end_grace(nn); |
7f5ef2e9 JL |
1089 | break; |
1090 | default: | |
1091 | return -EINVAL; | |
1092 | } | |
1093 | } | |
1094 | ||
1095 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%c\n", | |
1096 | nn->grace_ended ? 'Y' : 'N'); | |
1097 | } | |
1098 | ||
70c3b76c | 1099 | #endif |
0964a3d3 | 1100 | |
1da177e4 LT |
1101 | /*----------------------------------------------------------------------------*/ |
1102 | /* | |
1103 | * populating the filesystem. | |
1104 | */ | |
1105 | ||
e8a79fb1 BF |
1106 | /* Basically copying rpc_get_inode. */ |
1107 | static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode) | |
1108 | { | |
1109 | struct inode *inode = new_inode(sb); | |
1110 | if (!inode) | |
1111 | return NULL; | |
1112 | /* Following advice from simple_fill_super documentation: */ | |
1113 | inode->i_ino = iunique(sb, NFSD_MaxReserved); | |
1114 | inode->i_mode = mode; | |
11fec9b9 | 1115 | simple_inode_init_ts(inode); |
e8a79fb1 BF |
1116 | switch (mode & S_IFMT) { |
1117 | case S_IFDIR: | |
1118 | inode->i_fop = &simple_dir_operations; | |
1119 | inode->i_op = &simple_dir_inode_operations; | |
1120 | inc_nlink(inode); | |
76c50eb7 | 1121 | break; |
4df750c9 CL |
1122 | case S_IFLNK: |
1123 | inode->i_op = &simple_symlink_inode_operations; | |
1124 | break; | |
e8a79fb1 BF |
1125 | default: |
1126 | break; | |
1127 | } | |
1128 | return inode; | |
1129 | } | |
1130 | ||
bebd6997 | 1131 | static int __nfsd_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode, struct nfsdfs_client *ncl) |
e8a79fb1 BF |
1132 | { |
1133 | struct inode *inode; | |
1134 | ||
1135 | inode = nfsd_get_inode(dir->i_sb, mode); | |
1136 | if (!inode) | |
1137 | return -ENOMEM; | |
bebd6997 BF |
1138 | if (ncl) { |
1139 | inode->i_private = ncl; | |
1140 | kref_get(&ncl->cl_ref); | |
1141 | } | |
e8a79fb1 BF |
1142 | d_add(dentry, inode); |
1143 | inc_nlink(dir); | |
1144 | fsnotify_mkdir(dir, dentry); | |
1145 | return 0; | |
1146 | } | |
1147 | ||
1148 | static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name) | |
1149 | { | |
1150 | struct inode *dir = parent->d_inode; | |
1151 | struct dentry *dentry; | |
1152 | int ret = -ENOMEM; | |
1153 | ||
1154 | inode_lock(dir); | |
1155 | dentry = d_alloc_name(parent, name); | |
1156 | if (!dentry) | |
1157 | goto out_err; | |
bebd6997 | 1158 | ret = __nfsd_mkdir(d_inode(parent), dentry, S_IFDIR | 0600, ncl); |
e8a79fb1 BF |
1159 | if (ret) |
1160 | goto out_err; | |
e8a79fb1 BF |
1161 | out: |
1162 | inode_unlock(dir); | |
1163 | return dentry; | |
1164 | out_err: | |
d6846bfb | 1165 | dput(dentry); |
e8a79fb1 BF |
1166 | dentry = ERR_PTR(ret); |
1167 | goto out; | |
1168 | } | |
1169 | ||
4df750c9 CL |
1170 | #if IS_ENABLED(CONFIG_SUNRPC_GSS) |
1171 | static int __nfsd_symlink(struct inode *dir, struct dentry *dentry, | |
1172 | umode_t mode, const char *content) | |
1173 | { | |
1174 | struct inode *inode; | |
1175 | ||
1176 | inode = nfsd_get_inode(dir->i_sb, mode); | |
1177 | if (!inode) | |
1178 | return -ENOMEM; | |
1179 | ||
1180 | inode->i_link = (char *)content; | |
1181 | inode->i_size = strlen(content); | |
1182 | ||
1183 | d_add(dentry, inode); | |
1184 | inc_nlink(dir); | |
1185 | fsnotify_create(dir, dentry); | |
1186 | return 0; | |
1187 | } | |
1188 | ||
1189 | /* | |
1190 | * @content is assumed to be a NUL-terminated string that lives | |
1191 | * longer than the symlink itself. | |
1192 | */ | |
39d432fc CL |
1193 | static void _nfsd_symlink(struct dentry *parent, const char *name, |
1194 | const char *content) | |
4df750c9 CL |
1195 | { |
1196 | struct inode *dir = parent->d_inode; | |
1197 | struct dentry *dentry; | |
4b471a8b | 1198 | int ret; |
4df750c9 CL |
1199 | |
1200 | inode_lock(dir); | |
1201 | dentry = d_alloc_name(parent, name); | |
1202 | if (!dentry) | |
4b471a8b | 1203 | goto out; |
4df750c9 CL |
1204 | ret = __nfsd_symlink(d_inode(parent), dentry, S_IFLNK | 0777, content); |
1205 | if (ret) | |
4b471a8b | 1206 | dput(dentry); |
4df750c9 CL |
1207 | out: |
1208 | inode_unlock(dir); | |
4df750c9 CL |
1209 | } |
1210 | #else | |
39d432fc CL |
1211 | static inline void _nfsd_symlink(struct dentry *parent, const char *name, |
1212 | const char *content) | |
4df750c9 CL |
1213 | { |
1214 | } | |
1215 | ||
1216 | #endif | |
1217 | ||
b7a14708 | 1218 | static void clear_ncl(struct dentry *dentry) |
97ad4031 | 1219 | { |
b7a14708 | 1220 | struct inode *inode = d_inode(dentry); |
97ad4031 BF |
1221 | struct nfsdfs_client *ncl = inode->i_private; |
1222 | ||
b7a14708 | 1223 | spin_lock(&inode->i_lock); |
97ad4031 | 1224 | inode->i_private = NULL; |
b7a14708 | 1225 | spin_unlock(&inode->i_lock); |
97ad4031 BF |
1226 | kref_put(&ncl->cl_ref, ncl->cl_release); |
1227 | } | |
1228 | ||
97ad4031 BF |
1229 | struct nfsdfs_client *get_nfsdfs_client(struct inode *inode) |
1230 | { | |
1231 | struct nfsdfs_client *nc; | |
1232 | ||
b7a14708 AV |
1233 | spin_lock(&inode->i_lock); |
1234 | nc = inode->i_private; | |
1235 | if (nc) | |
1236 | kref_get(&nc->cl_ref); | |
1237 | spin_unlock(&inode->i_lock); | |
97ad4031 BF |
1238 | return nc; |
1239 | } | |
97ad4031 BF |
1240 | |
1241 | /* XXX: cut'n'paste from simple_fill_super; figure out if we could share | |
1242 | * code instead. */ | |
1243 | static int nfsdfs_create_files(struct dentry *root, | |
472d155a | 1244 | const struct tree_descr *files, |
b7a14708 | 1245 | struct nfsdfs_client *ncl, |
472d155a | 1246 | struct dentry **fdentries) |
97ad4031 BF |
1247 | { |
1248 | struct inode *dir = d_inode(root); | |
1249 | struct inode *inode; | |
1250 | struct dentry *dentry; | |
1251 | int i; | |
1252 | ||
1253 | inode_lock(dir); | |
1254 | for (i = 0; files->name && files->name[0]; i++, files++) { | |
97ad4031 BF |
1255 | dentry = d_alloc_name(root, files->name); |
1256 | if (!dentry) | |
1257 | goto out; | |
1258 | inode = nfsd_get_inode(d_inode(root)->i_sb, | |
1259 | S_IFREG | files->mode); | |
1260 | if (!inode) { | |
1261 | dput(dentry); | |
1262 | goto out; | |
1263 | } | |
b7a14708 | 1264 | kref_get(&ncl->cl_ref); |
97ad4031 | 1265 | inode->i_fop = files->ops; |
b7a14708 | 1266 | inode->i_private = ncl; |
97ad4031 BF |
1267 | d_add(dentry, inode); |
1268 | fsnotify_create(dir, dentry); | |
472d155a N |
1269 | if (fdentries) |
1270 | fdentries[i] = dentry; | |
97ad4031 BF |
1271 | } |
1272 | inode_unlock(dir); | |
1273 | return 0; | |
1274 | out: | |
97ad4031 BF |
1275 | inode_unlock(dir); |
1276 | return -ENOMEM; | |
1277 | } | |
1278 | ||
e8a79fb1 | 1279 | /* on success, returns positive number unique to that client. */ |
97ad4031 | 1280 | struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, |
472d155a N |
1281 | struct nfsdfs_client *ncl, u32 id, |
1282 | const struct tree_descr *files, | |
1283 | struct dentry **fdentries) | |
e8a79fb1 | 1284 | { |
97ad4031 | 1285 | struct dentry *dentry; |
e8a79fb1 | 1286 | char name[11]; |
97ad4031 | 1287 | int ret; |
e8a79fb1 | 1288 | |
bf5ed3e3 | 1289 | sprintf(name, "%u", id); |
e8a79fb1 | 1290 | |
97ad4031 BF |
1291 | dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); |
1292 | if (IS_ERR(dentry)) /* XXX: tossing errors? */ | |
1293 | return NULL; | |
b7a14708 | 1294 | ret = nfsdfs_create_files(dentry, files, ncl, fdentries); |
97ad4031 BF |
1295 | if (ret) { |
1296 | nfsd_client_rmdir(dentry); | |
1297 | return NULL; | |
1298 | } | |
1299 | return dentry; | |
e8a79fb1 BF |
1300 | } |
1301 | ||
1302 | /* Taken from __rpc_rmdir: */ | |
1303 | void nfsd_client_rmdir(struct dentry *dentry) | |
1304 | { | |
b7a14708 | 1305 | simple_recursive_removal(dentry, clear_ncl); |
e8a79fb1 BF |
1306 | } |
1307 | ||
96a374a3 | 1308 | static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) |
1da177e4 | 1309 | { |
e8a79fb1 BF |
1310 | struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, |
1311 | nfsd_net_id); | |
1312 | struct dentry *dentry; | |
1313 | int ret; | |
1314 | ||
cda37124 | 1315 | static const struct tree_descr nfsd_files[] = { |
96d851c4 | 1316 | [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, |
20ad856e AG |
1317 | /* Per-export io stats use same ops as exports file */ |
1318 | [NFSD_Export_Stats] = {"export_stats", &exports_nfsd_operations, S_IRUGO}, | |
e8e8753f | 1319 | [NFSD_Export_features] = {"export_features", |
9beeaab8 | 1320 | &export_features_fops, S_IRUGO}, |
4373ea84 WC |
1321 | [NFSD_FO_UnlockIP] = {"unlock_ip", |
1322 | &transaction_ops, S_IWUSR|S_IRUSR}, | |
17efa372 WC |
1323 | [NFSD_FO_UnlockFS] = {"unlock_filesystem", |
1324 | &transaction_ops, S_IWUSR|S_IRUSR}, | |
1da177e4 LT |
1325 | [NFSD_Fh] = {"filehandle", &transaction_ops, S_IWUSR|S_IRUSR}, |
1326 | [NFSD_Threads] = {"threads", &transaction_ops, S_IWUSR|S_IRUSR}, | |
eed2965a | 1327 | [NFSD_Pool_Threads] = {"pool_threads", &transaction_ops, S_IWUSR|S_IRUSR}, |
03cf6c9f | 1328 | [NFSD_Pool_Stats] = {"pool_stats", &pool_stats_operations, S_IRUGO}, |
64776611 C |
1329 | [NFSD_Reply_Cache_Stats] = {"reply_cache_stats", |
1330 | &nfsd_reply_cache_stats_fops, S_IRUGO}, | |
70c3b76c | 1331 | [NFSD_Versions] = {"versions", &transaction_ops, S_IWUSR|S_IRUSR}, |
80212d59 | 1332 | [NFSD_Ports] = {"portlist", &transaction_ops, S_IWUSR|S_IRUGO}, |
596bbe53 | 1333 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, |
1342f9dd | 1334 | [NFSD_Filecache] = {"filecache", &nfsd_file_cache_stats_fops, S_IRUGO}, |
1da177e4 LT |
1335 | #ifdef CONFIG_NFSD_V4 |
1336 | [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, | |
efc4bb4f | 1337 | [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, |
0770249b | 1338 | #ifdef CONFIG_NFSD_LEGACY_CLIENT_TRACKING |
0964a3d3 | 1339 | [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, |
0770249b | 1340 | #endif |
7f5ef2e9 | 1341 | [NFSD_V4EndGrace] = {"v4_end_grace", &transaction_ops, S_IWUSR|S_IRUGO}, |
1da177e4 LT |
1342 | #endif |
1343 | /* last one */ {""} | |
1344 | }; | |
96a374a3 | 1345 | |
e8a79fb1 BF |
1346 | ret = simple_fill_super(sb, 0x6e667364, nfsd_files); |
1347 | if (ret) | |
1348 | return ret; | |
39d432fc CL |
1349 | _nfsd_symlink(sb->s_root, "supported_krb5_enctypes", |
1350 | "/proc/net/rpc/gss_krb5_enctypes"); | |
e8a79fb1 BF |
1351 | dentry = nfsd_mkdir(sb->s_root, NULL, "clients"); |
1352 | if (IS_ERR(dentry)) | |
1353 | return PTR_ERR(dentry); | |
1354 | nn->nfsd_client_dir = dentry; | |
1355 | return 0; | |
1da177e4 | 1356 | } |
e8a79fb1 | 1357 | |
96a374a3 DH |
1358 | static int nfsd_fs_get_tree(struct fs_context *fc) |
1359 | { | |
533770cc | 1360 | return get_tree_keyed(fc, nfsd_fill_super, get_net(fc->net_ns)); |
1da177e4 LT |
1361 | } |
1362 | ||
96a374a3 | 1363 | static void nfsd_fs_free_fc(struct fs_context *fc) |
1da177e4 | 1364 | { |
96a374a3 DH |
1365 | if (fc->s_fs_info) |
1366 | put_net(fc->s_fs_info); | |
1367 | } | |
1368 | ||
1369 | static const struct fs_context_operations nfsd_fs_context_ops = { | |
1370 | .free = nfsd_fs_free_fc, | |
1371 | .get_tree = nfsd_fs_get_tree, | |
1372 | }; | |
1373 | ||
1374 | static int nfsd_init_fs_context(struct fs_context *fc) | |
1da177e4 | 1375 | { |
96a374a3 DH |
1376 | put_user_ns(fc->user_ns); |
1377 | fc->user_ns = get_user_ns(fc->net_ns->user_ns); | |
1378 | fc->ops = &nfsd_fs_context_ops; | |
1379 | return 0; | |
11f77942 SK |
1380 | } |
1381 | ||
1382 | static void nfsd_umount(struct super_block *sb) | |
1383 | { | |
1384 | struct net *net = sb->s_fs_info; | |
1385 | ||
c6c7f2a8 TM |
1386 | nfsd_shutdown_threads(net); |
1387 | ||
11f77942 SK |
1388 | kill_litter_super(sb); |
1389 | put_net(net); | |
1da177e4 LT |
1390 | } |
1391 | ||
1392 | static struct file_system_type nfsd_fs_type = { | |
1393 | .owner = THIS_MODULE, | |
1394 | .name = "nfsd", | |
96a374a3 | 1395 | .init_fs_context = nfsd_init_fs_context, |
11f77942 | 1396 | .kill_sb = nfsd_umount, |
1da177e4 | 1397 | }; |
7f78e035 | 1398 | MODULE_ALIAS_FS("nfsd"); |
1da177e4 | 1399 | |
e331f606 | 1400 | #ifdef CONFIG_PROC_FS |
340086da TR |
1401 | |
1402 | static int exports_proc_open(struct inode *inode, struct file *file) | |
1403 | { | |
1404 | return exports_net_open(current->nsproxy->net_ns, file); | |
1405 | } | |
1406 | ||
1407 | static const struct proc_ops exports_proc_ops = { | |
1408 | .proc_open = exports_proc_open, | |
1409 | .proc_read = seq_read, | |
1410 | .proc_lseek = seq_lseek, | |
1411 | .proc_release = seq_release, | |
1412 | }; | |
1413 | ||
e331f606 BF |
1414 | static int create_proc_exports_entry(void) |
1415 | { | |
1416 | struct proc_dir_entry *entry; | |
1417 | ||
1418 | entry = proc_mkdir("fs/nfs", NULL); | |
1419 | if (!entry) | |
1420 | return -ENOMEM; | |
97a32539 | 1421 | entry = proc_create("exports", 0, entry, &exports_proc_ops); |
ff7c4b36 | 1422 | if (!entry) { |
1423 | remove_proc_entry("fs/nfs", NULL); | |
e331f606 | 1424 | return -ENOMEM; |
ff7c4b36 | 1425 | } |
e331f606 BF |
1426 | return 0; |
1427 | } | |
1428 | #else /* CONFIG_PROC_FS */ | |
1429 | static int create_proc_exports_entry(void) | |
1430 | { | |
1431 | return 0; | |
1432 | } | |
1433 | #endif | |
1434 | ||
c7d03a00 | 1435 | unsigned int nfsd_net_id; |
5717e012 | 1436 | |
bd9d6a3e LB |
1437 | static int nfsd_genl_rpc_status_compose_msg(struct sk_buff *skb, |
1438 | struct netlink_callback *cb, | |
1439 | struct nfsd_genl_rqstp *rqstp) | |
1440 | { | |
1441 | void *hdr; | |
1442 | u32 i; | |
1443 | ||
1444 | hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, | |
1445 | &nfsd_nl_family, 0, NFSD_CMD_RPC_STATUS_GET); | |
1446 | if (!hdr) | |
1447 | return -ENOBUFS; | |
1448 | ||
1449 | if (nla_put_be32(skb, NFSD_A_RPC_STATUS_XID, rqstp->rq_xid) || | |
1450 | nla_put_u32(skb, NFSD_A_RPC_STATUS_FLAGS, rqstp->rq_flags) || | |
1451 | nla_put_u32(skb, NFSD_A_RPC_STATUS_PROG, rqstp->rq_prog) || | |
1452 | nla_put_u32(skb, NFSD_A_RPC_STATUS_PROC, rqstp->rq_proc) || | |
1453 | nla_put_u8(skb, NFSD_A_RPC_STATUS_VERSION, rqstp->rq_vers) || | |
1454 | nla_put_s64(skb, NFSD_A_RPC_STATUS_SERVICE_TIME, | |
1455 | ktime_to_us(rqstp->rq_stime), | |
1456 | NFSD_A_RPC_STATUS_PAD)) | |
1457 | return -ENOBUFS; | |
1458 | ||
1459 | switch (rqstp->rq_saddr.sa_family) { | |
1460 | case AF_INET: { | |
1461 | const struct sockaddr_in *s_in, *d_in; | |
1462 | ||
1463 | s_in = (const struct sockaddr_in *)&rqstp->rq_saddr; | |
1464 | d_in = (const struct sockaddr_in *)&rqstp->rq_daddr; | |
1465 | if (nla_put_in_addr(skb, NFSD_A_RPC_STATUS_SADDR4, | |
1466 | s_in->sin_addr.s_addr) || | |
1467 | nla_put_in_addr(skb, NFSD_A_RPC_STATUS_DADDR4, | |
1468 | d_in->sin_addr.s_addr) || | |
1469 | nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT, | |
1470 | s_in->sin_port) || | |
1471 | nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, | |
1472 | d_in->sin_port)) | |
1473 | return -ENOBUFS; | |
1474 | break; | |
1475 | } | |
1476 | case AF_INET6: { | |
1477 | const struct sockaddr_in6 *s_in, *d_in; | |
1478 | ||
1479 | s_in = (const struct sockaddr_in6 *)&rqstp->rq_saddr; | |
1480 | d_in = (const struct sockaddr_in6 *)&rqstp->rq_daddr; | |
1481 | if (nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_SADDR6, | |
1482 | &s_in->sin6_addr) || | |
1483 | nla_put_in6_addr(skb, NFSD_A_RPC_STATUS_DADDR6, | |
1484 | &d_in->sin6_addr) || | |
1485 | nla_put_be16(skb, NFSD_A_RPC_STATUS_SPORT, | |
1486 | s_in->sin6_port) || | |
1487 | nla_put_be16(skb, NFSD_A_RPC_STATUS_DPORT, | |
1488 | d_in->sin6_port)) | |
1489 | return -ENOBUFS; | |
1490 | break; | |
1491 | } | |
1492 | } | |
1493 | ||
1494 | for (i = 0; i < rqstp->rq_opcnt; i++) | |
1495 | if (nla_put_u32(skb, NFSD_A_RPC_STATUS_COMPOUND_OPS, | |
1496 | rqstp->rq_opnum[i])) | |
1497 | return -ENOBUFS; | |
1498 | ||
1499 | genlmsg_end(skb, hdr); | |
13727f85 LB |
1500 | return 0; |
1501 | } | |
1502 | ||
bd9d6a3e LB |
1503 | /** |
1504 | * nfsd_nl_rpc_status_get_dumpit - Handle rpc_status_get dumpit | |
1505 | * @skb: reply buffer | |
1506 | * @cb: netlink metadata and command arguments | |
1507 | * | |
1508 | * Returns the size of the reply or a negative errno. | |
1509 | */ | |
13727f85 LB |
1510 | int nfsd_nl_rpc_status_get_dumpit(struct sk_buff *skb, |
1511 | struct netlink_callback *cb) | |
1512 | { | |
bd9d6a3e | 1513 | int i, ret, rqstp_index = 0; |
da2c8fef LB |
1514 | struct nfsd_net *nn; |
1515 | ||
1516 | mutex_lock(&nfsd_mutex); | |
1517 | ||
1518 | nn = net_generic(sock_net(skb->sk), nfsd_net_id); | |
1519 | if (!nn->nfsd_serv) { | |
1520 | ret = -ENODEV; | |
1521 | goto out_unlock; | |
1522 | } | |
bd9d6a3e LB |
1523 | |
1524 | rcu_read_lock(); | |
1525 | ||
1526 | for (i = 0; i < nn->nfsd_serv->sv_nrpools; i++) { | |
1527 | struct svc_rqst *rqstp; | |
1528 | ||
1529 | if (i < cb->args[0]) /* already consumed */ | |
1530 | continue; | |
1531 | ||
1532 | rqstp_index = 0; | |
1533 | list_for_each_entry_rcu(rqstp, | |
1534 | &nn->nfsd_serv->sv_pools[i].sp_all_threads, | |
1535 | rq_all) { | |
1536 | struct nfsd_genl_rqstp genl_rqstp; | |
1537 | unsigned int status_counter; | |
1538 | ||
1539 | if (rqstp_index++ < cb->args[1]) /* already consumed */ | |
1540 | continue; | |
1541 | /* | |
1542 | * Acquire rq_status_counter before parsing the rqst | |
1543 | * fields. rq_status_counter is set to an odd value in | |
1544 | * order to notify the consumers the rqstp fields are | |
1545 | * meaningful. | |
1546 | */ | |
1547 | status_counter = | |
1548 | smp_load_acquire(&rqstp->rq_status_counter); | |
1549 | if (!(status_counter & 1)) | |
1550 | continue; | |
1551 | ||
1552 | genl_rqstp.rq_xid = rqstp->rq_xid; | |
1553 | genl_rqstp.rq_flags = rqstp->rq_flags; | |
1554 | genl_rqstp.rq_vers = rqstp->rq_vers; | |
1555 | genl_rqstp.rq_prog = rqstp->rq_prog; | |
1556 | genl_rqstp.rq_proc = rqstp->rq_proc; | |
1557 | genl_rqstp.rq_stime = rqstp->rq_stime; | |
1558 | genl_rqstp.rq_opcnt = 0; | |
1559 | memcpy(&genl_rqstp.rq_daddr, svc_daddr(rqstp), | |
1560 | sizeof(struct sockaddr)); | |
1561 | memcpy(&genl_rqstp.rq_saddr, svc_addr(rqstp), | |
1562 | sizeof(struct sockaddr)); | |
1563 | ||
1564 | #ifdef CONFIG_NFSD_V4 | |
1565 | if (rqstp->rq_vers == NFS4_VERSION && | |
1566 | rqstp->rq_proc == NFSPROC4_COMPOUND) { | |
1567 | /* NFSv4 compound */ | |
1568 | struct nfsd4_compoundargs *args; | |
1569 | int j; | |
1570 | ||
1571 | args = rqstp->rq_argp; | |
1572 | genl_rqstp.rq_opcnt = args->opcnt; | |
1573 | for (j = 0; j < genl_rqstp.rq_opcnt; j++) | |
1574 | genl_rqstp.rq_opnum[j] = | |
1575 | args->ops[j].opnum; | |
1576 | } | |
1577 | #endif /* CONFIG_NFSD_V4 */ | |
1578 | ||
1579 | /* | |
1580 | * Acquire rq_status_counter before reporting the rqst | |
1581 | * fields to the user. | |
1582 | */ | |
1583 | if (smp_load_acquire(&rqstp->rq_status_counter) != | |
1584 | status_counter) | |
1585 | continue; | |
1586 | ||
1587 | ret = nfsd_genl_rpc_status_compose_msg(skb, cb, | |
1588 | &genl_rqstp); | |
1589 | if (ret) | |
1590 | goto out; | |
1591 | } | |
1592 | } | |
1593 | ||
1594 | cb->args[0] = i; | |
1595 | cb->args[1] = rqstp_index; | |
1596 | ret = skb->len; | |
1597 | out: | |
1598 | rcu_read_unlock(); | |
da2c8fef | 1599 | out_unlock: |
bd9d6a3e LB |
1600 | mutex_unlock(&nfsd_mutex); |
1601 | ||
da2c8fef | 1602 | return ret; |
13727f85 LB |
1603 | } |
1604 | ||
924f4fb0 LB |
1605 | /** |
1606 | * nfsd_nl_threads_set_doit - set the number of running threads | |
1607 | * @skb: reply buffer | |
1608 | * @info: netlink metadata and command arguments | |
1609 | * | |
1610 | * Return 0 on success or a negative errno. | |
1611 | */ | |
1612 | int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) | |
1613 | { | |
7f5c330b | 1614 | int *nthreads, count = 0, nrpools, i, ret = -EOPNOTSUPP, rem; |
924f4fb0 LB |
1615 | struct net *net = genl_info_net(info); |
1616 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
1617 | const struct nlattr *attr; | |
1618 | const char *scope = NULL; | |
1619 | ||
1620 | if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_THREADS)) | |
1621 | return -EINVAL; | |
1622 | ||
1623 | /* count number of SERVER_THREADS values */ | |
1624 | nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) { | |
1625 | if (nla_type(attr) == NFSD_A_SERVER_THREADS) | |
1626 | count++; | |
1627 | } | |
1628 | ||
1629 | mutex_lock(&nfsd_mutex); | |
1630 | ||
7f5c330b JL |
1631 | nrpools = max(count, nfsd_nrpools(net)); |
1632 | nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL); | |
1633 | if (!nthreads) { | |
1634 | ret = -ENOMEM; | |
924f4fb0 | 1635 | goto out_unlock; |
7f5c330b JL |
1636 | } |
1637 | ||
1638 | i = 0; | |
1639 | nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) { | |
1640 | if (nla_type(attr) == NFSD_A_SERVER_THREADS) { | |
1641 | nthreads[i++] = nla_get_u32(attr); | |
1642 | if (i >= nrpools) | |
1643 | break; | |
1644 | } | |
1645 | } | |
924f4fb0 | 1646 | |
924f4fb0 LB |
1647 | if (info->attrs[NFSD_A_SERVER_GRACETIME] || |
1648 | info->attrs[NFSD_A_SERVER_LEASETIME] || | |
1649 | info->attrs[NFSD_A_SERVER_SCOPE]) { | |
1650 | ret = -EBUSY; | |
1651 | if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads) | |
1652 | goto out_unlock; | |
1653 | ||
1654 | ret = -EINVAL; | |
1655 | attr = info->attrs[NFSD_A_SERVER_GRACETIME]; | |
1656 | if (attr) { | |
1657 | u32 gracetime = nla_get_u32(attr); | |
1658 | ||
1659 | if (gracetime < 10 || gracetime > 3600) | |
1660 | goto out_unlock; | |
1661 | ||
1662 | nn->nfsd4_grace = gracetime; | |
1663 | } | |
1664 | ||
1665 | attr = info->attrs[NFSD_A_SERVER_LEASETIME]; | |
1666 | if (attr) { | |
1667 | u32 leasetime = nla_get_u32(attr); | |
1668 | ||
1669 | if (leasetime < 10 || leasetime > 3600) | |
1670 | goto out_unlock; | |
1671 | ||
1672 | nn->nfsd4_lease = leasetime; | |
1673 | } | |
1674 | ||
1675 | attr = info->attrs[NFSD_A_SERVER_SCOPE]; | |
1676 | if (attr) | |
1677 | scope = nla_data(attr); | |
1678 | } | |
1679 | ||
7f5c330b JL |
1680 | ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope); |
1681 | if (ret > 0) | |
1682 | ret = 0; | |
924f4fb0 LB |
1683 | out_unlock: |
1684 | mutex_unlock(&nfsd_mutex); | |
7f5c330b JL |
1685 | kfree(nthreads); |
1686 | return ret; | |
924f4fb0 LB |
1687 | } |
1688 | ||
1689 | /** | |
1690 | * nfsd_nl_threads_get_doit - get the number of running threads | |
1691 | * @skb: reply buffer | |
1692 | * @info: netlink metadata and command arguments | |
1693 | * | |
1694 | * Return 0 on success or a negative errno. | |
1695 | */ | |
1696 | int nfsd_nl_threads_get_doit(struct sk_buff *skb, struct genl_info *info) | |
1697 | { | |
1698 | struct net *net = genl_info_net(info); | |
1699 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
1700 | void *hdr; | |
1701 | int err; | |
1702 | ||
1703 | skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1704 | if (!skb) | |
1705 | return -ENOMEM; | |
1706 | ||
1707 | hdr = genlmsg_iput(skb, info); | |
1708 | if (!hdr) { | |
1709 | err = -EMSGSIZE; | |
1710 | goto err_free_msg; | |
1711 | } | |
1712 | ||
1713 | mutex_lock(&nfsd_mutex); | |
1714 | ||
1715 | err = nla_put_u32(skb, NFSD_A_SERVER_GRACETIME, | |
1716 | nn->nfsd4_grace) || | |
1717 | nla_put_u32(skb, NFSD_A_SERVER_LEASETIME, | |
1718 | nn->nfsd4_lease) || | |
1719 | nla_put_string(skb, NFSD_A_SERVER_SCOPE, | |
1720 | nn->nfsd_name); | |
1721 | if (err) | |
1722 | goto err_unlock; | |
1723 | ||
1724 | if (nn->nfsd_serv) { | |
1725 | int i; | |
1726 | ||
1727 | for (i = 0; i < nfsd_nrpools(net); ++i) { | |
1728 | struct svc_pool *sp = &nn->nfsd_serv->sv_pools[i]; | |
1729 | ||
1730 | err = nla_put_u32(skb, NFSD_A_SERVER_THREADS, | |
60749cbe | 1731 | sp->sp_nrthreads); |
924f4fb0 LB |
1732 | if (err) |
1733 | goto err_unlock; | |
1734 | } | |
1735 | } else { | |
1736 | err = nla_put_u32(skb, NFSD_A_SERVER_THREADS, 0); | |
1737 | if (err) | |
1738 | goto err_unlock; | |
1739 | } | |
1740 | ||
1741 | mutex_unlock(&nfsd_mutex); | |
1742 | ||
1743 | genlmsg_end(skb, hdr); | |
1744 | ||
1745 | return genlmsg_reply(skb, info); | |
1746 | ||
1747 | err_unlock: | |
1748 | mutex_unlock(&nfsd_mutex); | |
1749 | err_free_msg: | |
1750 | nlmsg_free(skb); | |
1751 | ||
1752 | return err; | |
1753 | } | |
1754 | ||
5a939bea LB |
1755 | /** |
1756 | * nfsd_nl_version_set_doit - set the nfs enabled versions | |
1757 | * @skb: reply buffer | |
1758 | * @info: netlink metadata and command arguments | |
1759 | * | |
1760 | * Return 0 on success or a negative errno. | |
1761 | */ | |
1762 | int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info) | |
1763 | { | |
1764 | const struct nlattr *attr; | |
1765 | struct nfsd_net *nn; | |
1766 | int i, rem; | |
1767 | ||
1768 | if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION)) | |
1769 | return -EINVAL; | |
1770 | ||
1771 | mutex_lock(&nfsd_mutex); | |
1772 | ||
1773 | nn = net_generic(genl_info_net(info), nfsd_net_id); | |
1774 | if (nn->nfsd_serv) { | |
1775 | mutex_unlock(&nfsd_mutex); | |
1776 | return -EBUSY; | |
1777 | } | |
1778 | ||
1779 | /* clear current supported versions. */ | |
1780 | nfsd_vers(nn, 2, NFSD_CLEAR); | |
1781 | nfsd_vers(nn, 3, NFSD_CLEAR); | |
1782 | for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++) | |
1783 | nfsd_minorversion(nn, i, NFSD_CLEAR); | |
1784 | ||
1785 | nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) { | |
1786 | struct nlattr *tb[NFSD_A_VERSION_MAX + 1]; | |
1787 | u32 major, minor = 0; | |
1788 | bool enabled; | |
1789 | ||
1790 | if (nla_type(attr) != NFSD_A_SERVER_PROTO_VERSION) | |
1791 | continue; | |
1792 | ||
1793 | if (nla_parse_nested(tb, NFSD_A_VERSION_MAX, attr, | |
1794 | nfsd_version_nl_policy, info->extack) < 0) | |
1795 | continue; | |
1796 | ||
1797 | if (!tb[NFSD_A_VERSION_MAJOR]) | |
1798 | continue; | |
1799 | ||
1800 | major = nla_get_u32(tb[NFSD_A_VERSION_MAJOR]); | |
1801 | if (tb[NFSD_A_VERSION_MINOR]) | |
1802 | minor = nla_get_u32(tb[NFSD_A_VERSION_MINOR]); | |
1803 | ||
1804 | enabled = nla_get_flag(tb[NFSD_A_VERSION_ENABLED]); | |
1805 | ||
1806 | switch (major) { | |
1807 | case 4: | |
1808 | nfsd_minorversion(nn, minor, enabled ? NFSD_SET : NFSD_CLEAR); | |
1809 | break; | |
1810 | case 3: | |
1811 | case 2: | |
1812 | if (!minor) | |
1813 | nfsd_vers(nn, major, enabled ? NFSD_SET : NFSD_CLEAR); | |
1814 | break; | |
1815 | default: | |
1816 | break; | |
1817 | } | |
1818 | } | |
1819 | ||
1820 | mutex_unlock(&nfsd_mutex); | |
1821 | ||
1822 | return 0; | |
1823 | } | |
1824 | ||
1825 | /** | |
1826 | * nfsd_nl_version_get_doit - get the enabled status for all supported nfs versions | |
1827 | * @skb: reply buffer | |
1828 | * @info: netlink metadata and command arguments | |
1829 | * | |
1830 | * Return 0 on success or a negative errno. | |
1831 | */ | |
1832 | int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info) | |
1833 | { | |
1834 | struct nfsd_net *nn; | |
1835 | int i, err; | |
1836 | void *hdr; | |
1837 | ||
1838 | skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
1839 | if (!skb) | |
1840 | return -ENOMEM; | |
1841 | ||
1842 | hdr = genlmsg_iput(skb, info); | |
1843 | if (!hdr) { | |
1844 | err = -EMSGSIZE; | |
1845 | goto err_free_msg; | |
1846 | } | |
1847 | ||
1848 | mutex_lock(&nfsd_mutex); | |
1849 | nn = net_generic(genl_info_net(info), nfsd_net_id); | |
1850 | ||
1851 | for (i = 2; i <= 4; i++) { | |
1852 | int j; | |
1853 | ||
1854 | for (j = 0; j <= NFSD_SUPPORTED_MINOR_VERSION; j++) { | |
1855 | struct nlattr *attr; | |
1856 | ||
1857 | /* Don't record any versions the kernel doesn't have | |
1858 | * compiled in | |
1859 | */ | |
1860 | if (!nfsd_support_version(i)) | |
1861 | continue; | |
1862 | ||
1863 | /* NFSv{2,3} does not support minor numbers */ | |
1864 | if (i < 4 && j) | |
1865 | continue; | |
1866 | ||
1867 | attr = nla_nest_start(skb, | |
1868 | NFSD_A_SERVER_PROTO_VERSION); | |
1869 | if (!attr) { | |
1870 | err = -EINVAL; | |
1871 | goto err_nfsd_unlock; | |
1872 | } | |
1873 | ||
1874 | if (nla_put_u32(skb, NFSD_A_VERSION_MAJOR, i) || | |
1875 | nla_put_u32(skb, NFSD_A_VERSION_MINOR, j)) { | |
1876 | err = -EINVAL; | |
1877 | goto err_nfsd_unlock; | |
1878 | } | |
1879 | ||
1880 | /* Set the enabled flag if the version is enabled */ | |
1881 | if (nfsd_vers(nn, i, NFSD_TEST) && | |
1882 | (i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) && | |
1883 | nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) { | |
1884 | err = -EINVAL; | |
1885 | goto err_nfsd_unlock; | |
1886 | } | |
1887 | ||
1888 | nla_nest_end(skb, attr); | |
1889 | } | |
1890 | } | |
1891 | ||
1892 | mutex_unlock(&nfsd_mutex); | |
1893 | genlmsg_end(skb, hdr); | |
1894 | ||
1895 | return genlmsg_reply(skb, info); | |
1896 | ||
1897 | err_nfsd_unlock: | |
1898 | mutex_unlock(&nfsd_mutex); | |
1899 | err_free_msg: | |
1900 | nlmsg_free(skb); | |
1901 | ||
1902 | return err; | |
1903 | } | |
1904 | ||
16a47117 LB |
1905 | /** |
1906 | * nfsd_nl_listener_set_doit - set the nfs running sockets | |
1907 | * @skb: reply buffer | |
1908 | * @info: netlink metadata and command arguments | |
1909 | * | |
1910 | * Return 0 on success or a negative errno. | |
1911 | */ | |
1912 | int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info) | |
1913 | { | |
1914 | struct net *net = genl_info_net(info); | |
1915 | struct svc_xprt *xprt, *tmp; | |
1916 | const struct nlattr *attr; | |
1917 | struct svc_serv *serv; | |
1918 | LIST_HEAD(permsocks); | |
1919 | struct nfsd_net *nn; | |
1920 | int err, rem; | |
1921 | ||
1922 | mutex_lock(&nfsd_mutex); | |
1923 | ||
1924 | err = nfsd_create_serv(net); | |
1925 | if (err) { | |
1926 | mutex_unlock(&nfsd_mutex); | |
1927 | return err; | |
1928 | } | |
1929 | ||
1930 | nn = net_generic(net, nfsd_net_id); | |
1931 | serv = nn->nfsd_serv; | |
1932 | ||
1933 | spin_lock_bh(&serv->sv_lock); | |
1934 | ||
1935 | /* Move all of the old listener sockets to a temp list */ | |
1936 | list_splice_init(&serv->sv_permsocks, &permsocks); | |
1937 | ||
1938 | /* | |
1939 | * Walk the list of server_socks from userland and move any that match | |
1940 | * back to sv_permsocks | |
1941 | */ | |
1942 | nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) { | |
1943 | struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; | |
1944 | const char *xcl_name; | |
1945 | struct sockaddr *sa; | |
1946 | ||
1947 | if (nla_type(attr) != NFSD_A_SERVER_SOCK_ADDR) | |
1948 | continue; | |
1949 | ||
1950 | if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, | |
1951 | nfsd_sock_nl_policy, info->extack) < 0) | |
1952 | continue; | |
1953 | ||
1954 | if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) | |
1955 | continue; | |
1956 | ||
1957 | if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) | |
1958 | continue; | |
1959 | ||
1960 | xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]); | |
1961 | sa = nla_data(tb[NFSD_A_SOCK_ADDR]); | |
1962 | ||
1963 | /* Put back any matching sockets */ | |
1964 | list_for_each_entry_safe(xprt, tmp, &permsocks, xpt_list) { | |
1965 | /* This shouldn't be possible */ | |
1966 | if (WARN_ON_ONCE(xprt->xpt_net != net)) { | |
1967 | list_move(&xprt->xpt_list, &serv->sv_permsocks); | |
1968 | continue; | |
1969 | } | |
1970 | ||
1971 | /* If everything matches, put it back */ | |
1972 | if (!strcmp(xprt->xpt_class->xcl_name, xcl_name) && | |
1973 | rpc_cmp_addr_port(sa, (struct sockaddr *)&xprt->xpt_local)) { | |
1974 | list_move(&xprt->xpt_list, &serv->sv_permsocks); | |
1975 | break; | |
1976 | } | |
1977 | } | |
1978 | } | |
1979 | ||
1980 | /* For now, no removing old sockets while server is running */ | |
1981 | if (serv->sv_nrthreads && !list_empty(&permsocks)) { | |
1982 | list_splice_init(&permsocks, &serv->sv_permsocks); | |
1983 | spin_unlock_bh(&serv->sv_lock); | |
1984 | err = -EBUSY; | |
1985 | goto out_unlock_mtx; | |
1986 | } | |
1987 | ||
1988 | /* Close the remaining sockets on the permsocks list */ | |
1989 | while (!list_empty(&permsocks)) { | |
1990 | xprt = list_first_entry(&permsocks, struct svc_xprt, xpt_list); | |
1991 | list_move(&xprt->xpt_list, &serv->sv_permsocks); | |
1992 | ||
1993 | /* | |
1994 | * Newly-created sockets are born with the BUSY bit set. Clear | |
1995 | * it if there are no threads, since nothing can pick it up | |
1996 | * in that case. | |
1997 | */ | |
1998 | if (!serv->sv_nrthreads) | |
1999 | clear_bit(XPT_BUSY, &xprt->xpt_flags); | |
2000 | ||
2001 | set_bit(XPT_CLOSE, &xprt->xpt_flags); | |
2002 | spin_unlock_bh(&serv->sv_lock); | |
2003 | svc_xprt_close(xprt); | |
2004 | spin_lock_bh(&serv->sv_lock); | |
2005 | } | |
2006 | ||
2007 | spin_unlock_bh(&serv->sv_lock); | |
2008 | ||
2009 | /* walk list of addrs again, open any that still don't exist */ | |
2010 | nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) { | |
2011 | struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; | |
2012 | const char *xcl_name; | |
2013 | struct sockaddr *sa; | |
2014 | int ret; | |
2015 | ||
2016 | if (nla_type(attr) != NFSD_A_SERVER_SOCK_ADDR) | |
2017 | continue; | |
2018 | ||
2019 | if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr, | |
2020 | nfsd_sock_nl_policy, info->extack) < 0) | |
2021 | continue; | |
2022 | ||
2023 | if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) | |
2024 | continue; | |
2025 | ||
2026 | if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) | |
2027 | continue; | |
2028 | ||
2029 | xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]); | |
2030 | sa = nla_data(tb[NFSD_A_SOCK_ADDR]); | |
2031 | ||
2032 | xprt = svc_find_listener(serv, xcl_name, net, sa); | |
2033 | if (xprt) { | |
2034 | svc_xprt_put(xprt); | |
2035 | continue; | |
2036 | } | |
2037 | ||
91da337e | 2038 | ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0, |
16a47117 LB |
2039 | get_current_cred()); |
2040 | /* always save the latest error */ | |
2041 | if (ret < 0) | |
2042 | err = ret; | |
2043 | } | |
2044 | ||
2045 | if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks)) | |
2046 | nfsd_destroy_serv(net); | |
2047 | ||
2048 | out_unlock_mtx: | |
2049 | mutex_unlock(&nfsd_mutex); | |
2050 | ||
2051 | return err; | |
2052 | } | |
2053 | ||
2054 | /** | |
2055 | * nfsd_nl_listener_get_doit - get the nfs running listeners | |
2056 | * @skb: reply buffer | |
2057 | * @info: netlink metadata and command arguments | |
2058 | * | |
2059 | * Return 0 on success or a negative errno. | |
2060 | */ | |
2061 | int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info) | |
2062 | { | |
2063 | struct svc_xprt *xprt; | |
2064 | struct svc_serv *serv; | |
2065 | struct nfsd_net *nn; | |
2066 | void *hdr; | |
2067 | int err; | |
2068 | ||
2069 | skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
2070 | if (!skb) | |
2071 | return -ENOMEM; | |
2072 | ||
2073 | hdr = genlmsg_iput(skb, info); | |
2074 | if (!hdr) { | |
2075 | err = -EMSGSIZE; | |
2076 | goto err_free_msg; | |
2077 | } | |
2078 | ||
2079 | mutex_lock(&nfsd_mutex); | |
2080 | nn = net_generic(genl_info_net(info), nfsd_net_id); | |
2081 | ||
2082 | /* no nfs server? Just send empty socket list */ | |
2083 | if (!nn->nfsd_serv) | |
2084 | goto out_unlock_mtx; | |
2085 | ||
2086 | serv = nn->nfsd_serv; | |
2087 | spin_lock_bh(&serv->sv_lock); | |
2088 | list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) { | |
2089 | struct nlattr *attr; | |
2090 | ||
2091 | attr = nla_nest_start(skb, NFSD_A_SERVER_SOCK_ADDR); | |
2092 | if (!attr) { | |
2093 | err = -EINVAL; | |
2094 | goto err_serv_unlock; | |
2095 | } | |
2096 | ||
2097 | if (nla_put_string(skb, NFSD_A_SOCK_TRANSPORT_NAME, | |
2098 | xprt->xpt_class->xcl_name) || | |
2099 | nla_put(skb, NFSD_A_SOCK_ADDR, | |
2100 | sizeof(struct sockaddr_storage), | |
2101 | &xprt->xpt_local)) { | |
2102 | err = -EINVAL; | |
2103 | goto err_serv_unlock; | |
2104 | } | |
2105 | ||
2106 | nla_nest_end(skb, attr); | |
2107 | } | |
2108 | spin_unlock_bh(&serv->sv_lock); | |
2109 | out_unlock_mtx: | |
2110 | mutex_unlock(&nfsd_mutex); | |
2111 | genlmsg_end(skb, hdr); | |
2112 | ||
2113 | return genlmsg_reply(skb, info); | |
2114 | ||
2115 | err_serv_unlock: | |
2116 | spin_unlock_bh(&serv->sv_lock); | |
2117 | mutex_unlock(&nfsd_mutex); | |
2118 | err_free_msg: | |
2119 | nlmsg_free(skb); | |
2120 | ||
2121 | return err; | |
2122 | } | |
2123 | ||
00506072 JL |
2124 | /** |
2125 | * nfsd_nl_pool_mode_set_doit - set the number of running threads | |
2126 | * @skb: reply buffer | |
2127 | * @info: netlink metadata and command arguments | |
2128 | * | |
2129 | * Return 0 on success or a negative errno. | |
2130 | */ | |
2131 | int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info) | |
2132 | { | |
2133 | const struct nlattr *attr; | |
2134 | ||
2135 | if (GENL_REQ_ATTR_CHECK(info, NFSD_A_POOL_MODE_MODE)) | |
2136 | return -EINVAL; | |
2137 | ||
2138 | attr = info->attrs[NFSD_A_POOL_MODE_MODE]; | |
2139 | return sunrpc_set_pool_mode(nla_data(attr)); | |
2140 | } | |
2141 | ||
2142 | /** | |
2143 | * nfsd_nl_pool_mode_get_doit - get info about pool_mode | |
2144 | * @skb: reply buffer | |
2145 | * @info: netlink metadata and command arguments | |
2146 | * | |
2147 | * Return 0 on success or a negative errno. | |
2148 | */ | |
2149 | int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info) | |
2150 | { | |
2151 | struct net *net = genl_info_net(info); | |
2152 | char buf[16]; | |
2153 | void *hdr; | |
2154 | int err; | |
2155 | ||
2156 | if (sunrpc_get_pool_mode(buf, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf)) | |
2157 | return -ERANGE; | |
2158 | ||
2159 | skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); | |
2160 | if (!skb) | |
2161 | return -ENOMEM; | |
2162 | ||
2163 | err = -EMSGSIZE; | |
2164 | hdr = genlmsg_iput(skb, info); | |
2165 | if (!hdr) | |
2166 | goto err_free_msg; | |
2167 | ||
2168 | err = nla_put_string(skb, NFSD_A_POOL_MODE_MODE, buf) | | |
2169 | nla_put_u32(skb, NFSD_A_POOL_MODE_NPOOLS, nfsd_nrpools(net)); | |
2170 | if (err) | |
2171 | goto err_free_msg; | |
2172 | ||
2173 | genlmsg_end(skb, hdr); | |
2174 | return genlmsg_reply(skb, info); | |
2175 | ||
2176 | err_free_msg: | |
2177 | nlmsg_free(skb); | |
2178 | return err; | |
2179 | } | |
2180 | ||
5e092be7 CL |
2181 | /** |
2182 | * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace | |
2183 | * @net: a freshly-created network namespace | |
2184 | * | |
2185 | * This information stays around as long as the network namespace is | |
2186 | * alive whether or not there is an NFSD instance running in the | |
2187 | * namespace. | |
2188 | * | |
2189 | * Returns zero on success, or a negative errno otherwise. | |
2190 | */ | |
2191 | static __net_init int nfsd_net_init(struct net *net) | |
5717e012 | 2192 | { |
3d733711 | 2193 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
73598a0c N |
2194 | int retval; |
2195 | int i; | |
5717e012 SK |
2196 | |
2197 | retval = nfsd_export_init(net); | |
2198 | if (retval) | |
2199 | goto out_export_error; | |
f69adb2f SK |
2200 | retval = nfsd_idmap_init(net); |
2201 | if (retval) | |
2202 | goto out_idmap_error; | |
7d12cce8 KW |
2203 | retval = percpu_counter_init_many(nn->counter, 0, GFP_KERNEL, |
2204 | NFSD_STATS_COUNTERS_NUM); | |
ed9ab734 JL |
2205 | if (retval) |
2206 | goto out_repcache_error; | |
16fb9808 | 2207 | memset(&nn->nfsd_svcstats, 0, sizeof(nn->nfsd_svcstats)); |
86ab08be | 2208 | nn->nfsd_svcstats.program = &nfsd_programs[0]; |
73598a0c N |
2209 | for (i = 0; i < sizeof(nn->nfsd_versions); i++) |
2210 | nn->nfsd_versions[i] = nfsd_support_version(i); | |
2211 | for (i = 0; i < sizeof(nn->nfsd4_minorversions); i++) | |
2212 | nn->nfsd4_minorversions[i] = nfsd_support_version(4); | |
e0011bca N |
2213 | nn->nfsd_info.mutex = &nfsd_mutex; |
2214 | nn->nfsd_serv = NULL; | |
f385f7d2 | 2215 | nfsd4_init_leases_net(nn); |
91d2e9b5 CL |
2216 | get_random_bytes(&nn->siphash_key, sizeof(nn->siphash_key)); |
2217 | seqlock_init(&nn->writeverf_lock); | |
93483ac5 | 2218 | nfsd_proc_stat_init(net); |
fa498386 | 2219 | #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
08580411 | 2220 | spin_lock_init(&nn->local_clients_lock); |
fa498386 WAA |
2221 | INIT_LIST_HEAD(&nn->local_clients); |
2222 | #endif | |
5717e012 SK |
2223 | return 0; |
2224 | ||
ed9ab734 JL |
2225 | out_repcache_error: |
2226 | nfsd_idmap_shutdown(net); | |
f69adb2f SK |
2227 | out_idmap_error: |
2228 | nfsd_export_shutdown(net); | |
5717e012 SK |
2229 | out_export_error: |
2230 | return retval; | |
2231 | } | |
2232 | ||
fa498386 WAA |
2233 | #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
2234 | /** | |
2235 | * nfsd_net_pre_exit - Disconnect localio clients from net namespace | |
2236 | * @net: a network namespace that is about to be destroyed | |
2237 | * | |
b49f049a | 2238 | * This invalidates ->net pointers held by localio clients |
fa498386 WAA |
2239 | * while they can still safely access nn->counter. |
2240 | */ | |
2241 | static __net_exit void nfsd_net_pre_exit(struct net *net) | |
2242 | { | |
2243 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); | |
2244 | ||
08580411 MS |
2245 | nfs_localio_invalidate_clients(&nn->local_clients, |
2246 | &nn->local_clients_lock); | |
fa498386 WAA |
2247 | } |
2248 | #endif | |
2249 | ||
5e092be7 CL |
2250 | /** |
2251 | * nfsd_net_exit - Release the nfsd_net portion of a net namespace | |
2252 | * @net: a network namespace that is about to be destroyed | |
2253 | * | |
2254 | */ | |
2255 | static __net_exit void nfsd_net_exit(struct net *net) | |
5717e012 | 2256 | { |
ed9ab734 JL |
2257 | struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
2258 | ||
93483ac5 | 2259 | nfsd_proc_stat_shutdown(net); |
7d12cce8 | 2260 | percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); |
f69adb2f | 2261 | nfsd_idmap_shutdown(net); |
5717e012 SK |
2262 | nfsd_export_shutdown(net); |
2263 | } | |
2264 | ||
7ea34ac1 | 2265 | static struct pernet_operations nfsd_net_ops = { |
5e092be7 | 2266 | .init = nfsd_net_init, |
fa498386 WAA |
2267 | #if IS_ENABLED(CONFIG_NFS_LOCALIO) |
2268 | .pre_exit = nfsd_net_pre_exit, | |
2269 | #endif | |
5e092be7 | 2270 | .exit = nfsd_net_exit, |
7ea34ac1 JL |
2271 | .id = &nfsd_net_id, |
2272 | .size = sizeof(struct nfsd_net), | |
2273 | }; | |
2274 | ||
1da177e4 LT |
2275 | static int __init init_nfsd(void) |
2276 | { | |
2277 | int retval; | |
1da177e4 | 2278 | |
bb7ffbf2 GC |
2279 | retval = nfsd4_init_slabs(); |
2280 | if (retval) | |
b10252c7 | 2281 | return retval; |
9cf514cc | 2282 | retval = nfsd4_init_pnfs(); |
65178db4 BS |
2283 | if (retval) |
2284 | goto out_free_slabs; | |
027690c7 BF |
2285 | retval = nfsd_drc_slab_create(); |
2286 | if (retval) | |
4b148854 | 2287 | goto out_free_pnfs; |
1da177e4 | 2288 | nfsd_lockd_init(); /* lockd->nfsd callbacks */ |
e331f606 BF |
2289 | retval = create_proc_exports_entry(); |
2290 | if (retval) | |
f69adb2f | 2291 | goto out_free_lockd; |
bd5ae928 BF |
2292 | retval = register_pernet_subsys(&nfsd_net_ops); |
2293 | if (retval < 0) | |
6f6f84aa | 2294 | goto out_free_exports; |
b10252c7 | 2295 | retval = register_cld_notifier(); |
d76cc46b | 2296 | if (retval) |
62fdb65e | 2297 | goto out_free_subsys; |
d76cc46b | 2298 | retval = nfsd4_create_laundry_wq(); |
6f6f84aa ZX |
2299 | if (retval) |
2300 | goto out_free_cld; | |
2301 | retval = register_filesystem(&nfsd_fs_type); | |
b10252c7 | 2302 | if (retval) |
26808d3f | 2303 | goto out_free_all; |
bd9d6a3e LB |
2304 | retval = genl_register_family(&nfsd_nl_family); |
2305 | if (retval) | |
2306 | goto out_free_all; | |
fa498386 | 2307 | nfsd_localio_ops_init(); |
bd9d6a3e | 2308 | |
26808d3f BF |
2309 | return 0; |
2310 | out_free_all: | |
6f6f84aa ZX |
2311 | nfsd4_destroy_laundry_wq(); |
2312 | out_free_cld: | |
62fdb65e ZX |
2313 | unregister_cld_notifier(); |
2314 | out_free_subsys: | |
b10252c7 | 2315 | unregister_pernet_subsys(&nfsd_net_ops); |
bd5ae928 | 2316 | out_free_exports: |
26808d3f BF |
2317 | remove_proc_entry("fs/nfs/exports", NULL); |
2318 | remove_proc_entry("fs/nfs", NULL); | |
dbf847ec | 2319 | out_free_lockd: |
26808d3f | 2320 | nfsd_lockd_shutdown(); |
027690c7 | 2321 | nfsd_drc_slab_free(); |
e567b98c | 2322 | out_free_pnfs: |
9cf514cc | 2323 | nfsd4_exit_pnfs(); |
65178db4 | 2324 | out_free_slabs: |
26808d3f | 2325 | nfsd4_free_slabs(); |
1da177e4 LT |
2326 | return retval; |
2327 | } | |
2328 | ||
2329 | static void __exit exit_nfsd(void) | |
2330 | { | |
bd9d6a3e | 2331 | genl_unregister_family(&nfsd_nl_family); |
6f6f84aa | 2332 | unregister_filesystem(&nfsd_fs_type); |
d76cc46b | 2333 | nfsd4_destroy_laundry_wq(); |
b10252c7 | 2334 | unregister_cld_notifier(); |
bd5ae928 | 2335 | unregister_pernet_subsys(&nfsd_net_ops); |
027690c7 | 2336 | nfsd_drc_slab_free(); |
1da177e4 LT |
2337 | remove_proc_entry("fs/nfs/exports", NULL); |
2338 | remove_proc_entry("fs/nfs", NULL); | |
1da177e4 | 2339 | nfsd_lockd_shutdown(); |
e8ff2a84 | 2340 | nfsd4_free_slabs(); |
9cf514cc | 2341 | nfsd4_exit_pnfs(); |
1da177e4 LT |
2342 | } |
2343 | ||
2344 | MODULE_AUTHOR("Olaf Kirch <[email protected]>"); | |
5865bafa | 2345 | MODULE_DESCRIPTION("In-kernel NFS server"); |
1da177e4 LT |
2346 | MODULE_LICENSE("GPL"); |
2347 | module_init(init_nfsd) | |
2348 | module_exit(exit_nfsd) |