| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Syscall interface to knfsd. |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 7 | 8 | #include <linux/slab.h> |
|---|
| 8 | 9 | #include <linux/namei.h> |
|---|
| 9 | 10 | #include <linux/ctype.h> |
|---|
| 11 | +#include <linux/fs_context.h> |
|---|
| 10 | 12 | |
|---|
| 11 | 13 | #include <linux/sunrpc/svcsock.h> |
|---|
| 12 | 14 | #include <linux/lockd/lockd.h> |
|---|
| .. | .. |
|---|
| 15 | 17 | #include <linux/sunrpc/gss_krb5_enctypes.h> |
|---|
| 16 | 18 | #include <linux/sunrpc/rpc_pipe_fs.h> |
|---|
| 17 | 19 | #include <linux/module.h> |
|---|
| 20 | +#include <linux/fsnotify.h> |
|---|
| 18 | 21 | |
|---|
| 19 | 22 | #include "idmap.h" |
|---|
| 20 | 23 | #include "nfsd.h" |
|---|
| .. | .. |
|---|
| 52 | 55 | NFSD_RecoveryDir, |
|---|
| 53 | 56 | NFSD_V4EndGrace, |
|---|
| 54 | 57 | #endif |
|---|
| 58 | + NFSD_MaxReserved |
|---|
| 55 | 59 | }; |
|---|
| 56 | 60 | |
|---|
| 57 | 61 | /* |
|---|
| .. | .. |
|---|
| 153 | 157 | return exports_net_open(current->nsproxy->net_ns, file); |
|---|
| 154 | 158 | } |
|---|
| 155 | 159 | |
|---|
| 156 | | -static const struct file_operations exports_proc_operations = { |
|---|
| 157 | | - .open = exports_proc_open, |
|---|
| 158 | | - .read = seq_read, |
|---|
| 159 | | - .llseek = seq_lseek, |
|---|
| 160 | | - .release = seq_release, |
|---|
| 160 | +static const struct proc_ops exports_proc_ops = { |
|---|
| 161 | + .proc_open = exports_proc_open, |
|---|
| 162 | + .proc_read = seq_read, |
|---|
| 163 | + .proc_lseek = seq_lseek, |
|---|
| 164 | + .proc_release = seq_release, |
|---|
| 161 | 165 | }; |
|---|
| 162 | 166 | |
|---|
| 163 | 167 | static int exports_nfsd_open(struct inode *inode, struct file *file) |
|---|
| .. | .. |
|---|
| 234 | 238 | return file_inode(file)->i_sb->s_fs_info; |
|---|
| 235 | 239 | } |
|---|
| 236 | 240 | |
|---|
| 237 | | -/** |
|---|
| 241 | +/* |
|---|
| 238 | 242 | * write_unlock_ip - Release all locks used by a client |
|---|
| 239 | 243 | * |
|---|
| 240 | 244 | * Experimental. |
|---|
| .. | .. |
|---|
| 273 | 277 | return nlmsvc_unlock_all_by_ip(sap); |
|---|
| 274 | 278 | } |
|---|
| 275 | 279 | |
|---|
| 276 | | -/** |
|---|
| 280 | +/* |
|---|
| 277 | 281 | * write_unlock_fs - Release all locks on a local file system |
|---|
| 278 | 282 | * |
|---|
| 279 | 283 | * Experimental. |
|---|
| .. | .. |
|---|
| 323 | 327 | return error; |
|---|
| 324 | 328 | } |
|---|
| 325 | 329 | |
|---|
| 326 | | -/** |
|---|
| 330 | +/* |
|---|
| 327 | 331 | * write_filehandle - Get a variable-length NFS file handle by path |
|---|
| 328 | 332 | * |
|---|
| 329 | 333 | * On input, the buffer contains a '\n'-terminated C string comprised of |
|---|
| .. | .. |
|---|
| 347 | 351 | static ssize_t write_filehandle(struct file *file, char *buf, size_t size) |
|---|
| 348 | 352 | { |
|---|
| 349 | 353 | char *dname, *path; |
|---|
| 350 | | - int uninitialized_var(maxsize); |
|---|
| 354 | + int maxsize; |
|---|
| 351 | 355 | char *mesg = buf; |
|---|
| 352 | 356 | int len; |
|---|
| 353 | 357 | struct auth_domain *dom; |
|---|
| .. | .. |
|---|
| 398 | 402 | return mesg - buf; |
|---|
| 399 | 403 | } |
|---|
| 400 | 404 | |
|---|
| 401 | | -/** |
|---|
| 405 | +/* |
|---|
| 402 | 406 | * write_threads - Start NFSD, or report the current number of running threads |
|---|
| 403 | 407 | * |
|---|
| 404 | 408 | * Input: |
|---|
| .. | .. |
|---|
| 439 | 443 | return rv; |
|---|
| 440 | 444 | if (newthreads < 0) |
|---|
| 441 | 445 | return -EINVAL; |
|---|
| 442 | | - rv = nfsd_svc(newthreads, net); |
|---|
| 446 | + rv = nfsd_svc(newthreads, net, file->f_cred); |
|---|
| 443 | 447 | if (rv < 0) |
|---|
| 444 | 448 | return rv; |
|---|
| 445 | 449 | } else |
|---|
| .. | .. |
|---|
| 448 | 452 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); |
|---|
| 449 | 453 | } |
|---|
| 450 | 454 | |
|---|
| 451 | | -/** |
|---|
| 455 | +/* |
|---|
| 452 | 456 | * write_pool_threads - Set or report the current number of threads per pool |
|---|
| 453 | 457 | * |
|---|
| 454 | 458 | * Input: |
|---|
| .. | .. |
|---|
| 537 | 541 | } |
|---|
| 538 | 542 | |
|---|
| 539 | 543 | static ssize_t |
|---|
| 540 | | -nfsd_print_version_support(char *buf, int remaining, const char *sep, |
|---|
| 541 | | - unsigned vers, int minor) |
|---|
| 544 | +nfsd_print_version_support(struct nfsd_net *nn, char *buf, int remaining, |
|---|
| 545 | + const char *sep, unsigned vers, int minor) |
|---|
| 542 | 546 | { |
|---|
| 543 | 547 | const char *format = minor < 0 ? "%s%c%u" : "%s%c%u.%u"; |
|---|
| 544 | | - bool supported = !!nfsd_vers(vers, NFSD_TEST); |
|---|
| 548 | + bool supported = !!nfsd_vers(nn, vers, NFSD_TEST); |
|---|
| 545 | 549 | |
|---|
| 546 | 550 | if (vers == 4 && minor >= 0 && |
|---|
| 547 | | - !nfsd_minorversion(minor, NFSD_TEST)) |
|---|
| 551 | + !nfsd_minorversion(nn, minor, NFSD_TEST)) |
|---|
| 548 | 552 | supported = false; |
|---|
| 549 | 553 | if (minor == 0 && supported) |
|---|
| 550 | 554 | /* |
|---|
| .. | .. |
|---|
| 599 | 603 | switch(num) { |
|---|
| 600 | 604 | case 2: |
|---|
| 601 | 605 | case 3: |
|---|
| 602 | | - nfsd_vers(num, cmd); |
|---|
| 606 | + nfsd_vers(nn, num, cmd); |
|---|
| 603 | 607 | break; |
|---|
| 604 | 608 | case 4: |
|---|
| 605 | 609 | if (*minorp == '.') { |
|---|
| 606 | | - if (nfsd_minorversion(minor, cmd) < 0) |
|---|
| 610 | + if (nfsd_minorversion(nn, minor, cmd) < 0) |
|---|
| 607 | 611 | return -EINVAL; |
|---|
| 608 | | - } else if ((cmd == NFSD_SET) != nfsd_vers(num, NFSD_TEST)) { |
|---|
| 612 | + } else if ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { |
|---|
| 609 | 613 | /* |
|---|
| 610 | 614 | * Either we have +4 and no minors are enabled, |
|---|
| 611 | 615 | * or we have -4 and at least one minor is enabled. |
|---|
| 612 | 616 | * In either case, propagate 'cmd' to all minors. |
|---|
| 613 | 617 | */ |
|---|
| 614 | 618 | minor = 0; |
|---|
| 615 | | - while (nfsd_minorversion(minor, cmd) >= 0) |
|---|
| 619 | + while (nfsd_minorversion(nn, minor, cmd) >= 0) |
|---|
| 616 | 620 | minor++; |
|---|
| 617 | 621 | } |
|---|
| 618 | 622 | break; |
|---|
| .. | .. |
|---|
| 624 | 628 | /* If all get turned off, turn them back on, as |
|---|
| 625 | 629 | * having no versions is BAD |
|---|
| 626 | 630 | */ |
|---|
| 627 | | - nfsd_reset_versions(); |
|---|
| 631 | + nfsd_reset_versions(nn); |
|---|
| 628 | 632 | } |
|---|
| 629 | 633 | |
|---|
| 630 | 634 | /* Now write current state into reply buffer */ |
|---|
| .. | .. |
|---|
| 633 | 637 | remaining = SIMPLE_TRANSACTION_LIMIT; |
|---|
| 634 | 638 | for (num=2 ; num <= 4 ; num++) { |
|---|
| 635 | 639 | int minor; |
|---|
| 636 | | - if (!nfsd_vers(num, NFSD_AVAIL)) |
|---|
| 640 | + if (!nfsd_vers(nn, num, NFSD_AVAIL)) |
|---|
| 637 | 641 | continue; |
|---|
| 638 | 642 | |
|---|
| 639 | 643 | minor = -1; |
|---|
| 640 | 644 | do { |
|---|
| 641 | | - len = nfsd_print_version_support(buf, remaining, |
|---|
| 645 | + len = nfsd_print_version_support(nn, buf, remaining, |
|---|
| 642 | 646 | sep, num, minor); |
|---|
| 643 | 647 | if (len >= remaining) |
|---|
| 644 | 648 | goto out; |
|---|
| .. | .. |
|---|
| 657 | 661 | return tlen + len; |
|---|
| 658 | 662 | } |
|---|
| 659 | 663 | |
|---|
| 660 | | -/** |
|---|
| 664 | +/* |
|---|
| 661 | 665 | * write_versions - Set or report the available NFS protocol versions |
|---|
| 662 | 666 | * |
|---|
| 663 | 667 | * Input: |
|---|
| .. | .. |
|---|
| 717 | 721 | * a socket of a supported family/protocol, and we use it as an |
|---|
| 718 | 722 | * nfsd listener. |
|---|
| 719 | 723 | */ |
|---|
| 720 | | -static ssize_t __write_ports_addfd(char *buf, struct net *net) |
|---|
| 724 | +static ssize_t __write_ports_addfd(char *buf, struct net *net, const struct cred *cred) |
|---|
| 721 | 725 | { |
|---|
| 722 | 726 | char *mesg = buf; |
|---|
| 723 | 727 | int fd, err; |
|---|
| .. | .. |
|---|
| 736 | 740 | if (err != 0) |
|---|
| 737 | 741 | return err; |
|---|
| 738 | 742 | |
|---|
| 739 | | - err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); |
|---|
| 743 | + err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT, cred); |
|---|
| 740 | 744 | if (err < 0) { |
|---|
| 741 | 745 | nfsd_destroy(net); |
|---|
| 742 | 746 | return err; |
|---|
| .. | .. |
|---|
| 751 | 755 | * A transport listener is added by writing it's transport name and |
|---|
| 752 | 756 | * a port number. |
|---|
| 753 | 757 | */ |
|---|
| 754 | | -static ssize_t __write_ports_addxprt(char *buf, struct net *net) |
|---|
| 758 | +static ssize_t __write_ports_addxprt(char *buf, struct net *net, const struct cred *cred) |
|---|
| 755 | 759 | { |
|---|
| 756 | 760 | char transport[16]; |
|---|
| 757 | 761 | struct svc_xprt *xprt; |
|---|
| .. | .. |
|---|
| 769 | 773 | return err; |
|---|
| 770 | 774 | |
|---|
| 771 | 775 | err = svc_create_xprt(nn->nfsd_serv, transport, net, |
|---|
| 772 | | - PF_INET, port, SVC_SOCK_ANONYMOUS); |
|---|
| 776 | + PF_INET, port, SVC_SOCK_ANONYMOUS, cred); |
|---|
| 773 | 777 | if (err < 0) |
|---|
| 774 | 778 | goto out_err; |
|---|
| 775 | 779 | |
|---|
| 776 | 780 | err = svc_create_xprt(nn->nfsd_serv, transport, net, |
|---|
| 777 | | - PF_INET6, port, SVC_SOCK_ANONYMOUS); |
|---|
| 781 | + PF_INET6, port, SVC_SOCK_ANONYMOUS, cred); |
|---|
| 778 | 782 | if (err < 0 && err != -EAFNOSUPPORT) |
|---|
| 779 | 783 | goto out_close; |
|---|
| 780 | 784 | |
|---|
| .. | .. |
|---|
| 802 | 806 | return __write_ports_names(buf, net); |
|---|
| 803 | 807 | |
|---|
| 804 | 808 | if (isdigit(buf[0])) |
|---|
| 805 | | - return __write_ports_addfd(buf, net); |
|---|
| 809 | + return __write_ports_addfd(buf, net, file->f_cred); |
|---|
| 806 | 810 | |
|---|
| 807 | 811 | if (isalpha(buf[0])) |
|---|
| 808 | | - return __write_ports_addxprt(buf, net); |
|---|
| 812 | + return __write_ports_addxprt(buf, net, file->f_cred); |
|---|
| 809 | 813 | |
|---|
| 810 | 814 | return -EINVAL; |
|---|
| 811 | 815 | } |
|---|
| 812 | 816 | |
|---|
| 813 | | -/** |
|---|
| 817 | +/* |
|---|
| 814 | 818 | * write_ports - Pass a socket file descriptor or transport name to listen on |
|---|
| 815 | 819 | * |
|---|
| 816 | 820 | * Input: |
|---|
| .. | .. |
|---|
| 866 | 870 | |
|---|
| 867 | 871 | int nfsd_max_blksize; |
|---|
| 868 | 872 | |
|---|
| 869 | | -/** |
|---|
| 873 | +/* |
|---|
| 870 | 874 | * write_maxblksize - Set or report the current NFS blksize |
|---|
| 871 | 875 | * |
|---|
| 872 | 876 | * Input: |
|---|
| .. | .. |
|---|
| 916 | 920 | nfsd_max_blksize); |
|---|
| 917 | 921 | } |
|---|
| 918 | 922 | |
|---|
| 919 | | -/** |
|---|
| 923 | +/* |
|---|
| 920 | 924 | * write_maxconn - Set or report the current max number of connections |
|---|
| 921 | 925 | * |
|---|
| 922 | 926 | * Input: |
|---|
| .. | .. |
|---|
| 955 | 959 | |
|---|
| 956 | 960 | #ifdef CONFIG_NFSD_V4 |
|---|
| 957 | 961 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, |
|---|
| 958 | | - time_t *time, struct nfsd_net *nn) |
|---|
| 962 | + time64_t *time, struct nfsd_net *nn) |
|---|
| 959 | 963 | { |
|---|
| 960 | 964 | char *mesg = buf; |
|---|
| 961 | 965 | int rv, i; |
|---|
| .. | .. |
|---|
| 983 | 987 | *time = i; |
|---|
| 984 | 988 | } |
|---|
| 985 | 989 | |
|---|
| 986 | | - return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); |
|---|
| 990 | + return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%lld\n", *time); |
|---|
| 987 | 991 | } |
|---|
| 988 | 992 | |
|---|
| 989 | 993 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, |
|---|
| 990 | | - time_t *time, struct nfsd_net *nn) |
|---|
| 994 | + time64_t *time, struct nfsd_net *nn) |
|---|
| 991 | 995 | { |
|---|
| 992 | 996 | ssize_t rv; |
|---|
| 993 | 997 | |
|---|
| .. | .. |
|---|
| 997 | 1001 | return rv; |
|---|
| 998 | 1002 | } |
|---|
| 999 | 1003 | |
|---|
| 1000 | | -/** |
|---|
| 1004 | +/* |
|---|
| 1001 | 1005 | * write_leasetime - Set or report the current NFSv4 lease time |
|---|
| 1002 | 1006 | * |
|---|
| 1003 | 1007 | * Input: |
|---|
| .. | .. |
|---|
| 1024 | 1028 | return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); |
|---|
| 1025 | 1029 | } |
|---|
| 1026 | 1030 | |
|---|
| 1027 | | -/** |
|---|
| 1031 | +/* |
|---|
| 1028 | 1032 | * write_gracetime - Set or report current NFSv4 grace period time |
|---|
| 1029 | 1033 | * |
|---|
| 1030 | 1034 | * As above, but sets the time of the NFSv4 grace period. |
|---|
| .. | .. |
|---|
| 1068 | 1072 | nfs4_recoverydir()); |
|---|
| 1069 | 1073 | } |
|---|
| 1070 | 1074 | |
|---|
| 1071 | | -/** |
|---|
| 1075 | +/* |
|---|
| 1072 | 1076 | * write_recoverydir - Set or report the pathname of the recovery directory |
|---|
| 1073 | 1077 | * |
|---|
| 1074 | 1078 | * Input: |
|---|
| .. | .. |
|---|
| 1100 | 1104 | return rv; |
|---|
| 1101 | 1105 | } |
|---|
| 1102 | 1106 | |
|---|
| 1103 | | -/** |
|---|
| 1107 | +/* |
|---|
| 1104 | 1108 | * write_v4_end_grace - release grace period for nfsd's v4.x lock manager |
|---|
| 1105 | 1109 | * |
|---|
| 1106 | 1110 | * Input: |
|---|
| .. | .. |
|---|
| 1149 | 1153 | * populating the filesystem. |
|---|
| 1150 | 1154 | */ |
|---|
| 1151 | 1155 | |
|---|
| 1152 | | -static int nfsd_fill_super(struct super_block * sb, void * data, int silent) |
|---|
| 1156 | +/* Basically copying rpc_get_inode. */ |
|---|
| 1157 | +static struct inode *nfsd_get_inode(struct super_block *sb, umode_t mode) |
|---|
| 1153 | 1158 | { |
|---|
| 1159 | + struct inode *inode = new_inode(sb); |
|---|
| 1160 | + if (!inode) |
|---|
| 1161 | + return NULL; |
|---|
| 1162 | + /* Following advice from simple_fill_super documentation: */ |
|---|
| 1163 | + inode->i_ino = iunique(sb, NFSD_MaxReserved); |
|---|
| 1164 | + inode->i_mode = mode; |
|---|
| 1165 | + inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode); |
|---|
| 1166 | + switch (mode & S_IFMT) { |
|---|
| 1167 | + case S_IFDIR: |
|---|
| 1168 | + inode->i_fop = &simple_dir_operations; |
|---|
| 1169 | + inode->i_op = &simple_dir_inode_operations; |
|---|
| 1170 | + inc_nlink(inode); |
|---|
| 1171 | + default: |
|---|
| 1172 | + break; |
|---|
| 1173 | + } |
|---|
| 1174 | + return inode; |
|---|
| 1175 | +} |
|---|
| 1176 | + |
|---|
| 1177 | +static int __nfsd_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode, struct nfsdfs_client *ncl) |
|---|
| 1178 | +{ |
|---|
| 1179 | + struct inode *inode; |
|---|
| 1180 | + |
|---|
| 1181 | + inode = nfsd_get_inode(dir->i_sb, mode); |
|---|
| 1182 | + if (!inode) |
|---|
| 1183 | + return -ENOMEM; |
|---|
| 1184 | + if (ncl) { |
|---|
| 1185 | + inode->i_private = ncl; |
|---|
| 1186 | + kref_get(&ncl->cl_ref); |
|---|
| 1187 | + } |
|---|
| 1188 | + d_add(dentry, inode); |
|---|
| 1189 | + inc_nlink(dir); |
|---|
| 1190 | + fsnotify_mkdir(dir, dentry); |
|---|
| 1191 | + return 0; |
|---|
| 1192 | +} |
|---|
| 1193 | + |
|---|
| 1194 | +static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name) |
|---|
| 1195 | +{ |
|---|
| 1196 | + struct inode *dir = parent->d_inode; |
|---|
| 1197 | + struct dentry *dentry; |
|---|
| 1198 | + int ret = -ENOMEM; |
|---|
| 1199 | + |
|---|
| 1200 | + inode_lock(dir); |
|---|
| 1201 | + dentry = d_alloc_name(parent, name); |
|---|
| 1202 | + if (!dentry) |
|---|
| 1203 | + goto out_err; |
|---|
| 1204 | + ret = __nfsd_mkdir(d_inode(parent), dentry, S_IFDIR | 0600, ncl); |
|---|
| 1205 | + if (ret) |
|---|
| 1206 | + goto out_err; |
|---|
| 1207 | +out: |
|---|
| 1208 | + inode_unlock(dir); |
|---|
| 1209 | + return dentry; |
|---|
| 1210 | +out_err: |
|---|
| 1211 | + dput(dentry); |
|---|
| 1212 | + dentry = ERR_PTR(ret); |
|---|
| 1213 | + goto out; |
|---|
| 1214 | +} |
|---|
| 1215 | + |
|---|
| 1216 | +static void clear_ncl(struct inode *inode) |
|---|
| 1217 | +{ |
|---|
| 1218 | + struct nfsdfs_client *ncl = inode->i_private; |
|---|
| 1219 | + |
|---|
| 1220 | + inode->i_private = NULL; |
|---|
| 1221 | + kref_put(&ncl->cl_ref, ncl->cl_release); |
|---|
| 1222 | +} |
|---|
| 1223 | + |
|---|
| 1224 | +static struct nfsdfs_client *__get_nfsdfs_client(struct inode *inode) |
|---|
| 1225 | +{ |
|---|
| 1226 | + struct nfsdfs_client *nc = inode->i_private; |
|---|
| 1227 | + |
|---|
| 1228 | + if (nc) |
|---|
| 1229 | + kref_get(&nc->cl_ref); |
|---|
| 1230 | + return nc; |
|---|
| 1231 | +} |
|---|
| 1232 | + |
|---|
| 1233 | +struct nfsdfs_client *get_nfsdfs_client(struct inode *inode) |
|---|
| 1234 | +{ |
|---|
| 1235 | + struct nfsdfs_client *nc; |
|---|
| 1236 | + |
|---|
| 1237 | + inode_lock_shared(inode); |
|---|
| 1238 | + nc = __get_nfsdfs_client(inode); |
|---|
| 1239 | + inode_unlock_shared(inode); |
|---|
| 1240 | + return nc; |
|---|
| 1241 | +} |
|---|
| 1242 | +/* from __rpc_unlink */ |
|---|
| 1243 | +static void nfsdfs_remove_file(struct inode *dir, struct dentry *dentry) |
|---|
| 1244 | +{ |
|---|
| 1245 | + int ret; |
|---|
| 1246 | + |
|---|
| 1247 | + clear_ncl(d_inode(dentry)); |
|---|
| 1248 | + dget(dentry); |
|---|
| 1249 | + ret = simple_unlink(dir, dentry); |
|---|
| 1250 | + d_drop(dentry); |
|---|
| 1251 | + fsnotify_unlink(dir, dentry); |
|---|
| 1252 | + dput(dentry); |
|---|
| 1253 | + WARN_ON_ONCE(ret); |
|---|
| 1254 | +} |
|---|
| 1255 | + |
|---|
| 1256 | +static void nfsdfs_remove_files(struct dentry *root) |
|---|
| 1257 | +{ |
|---|
| 1258 | + struct dentry *dentry, *tmp; |
|---|
| 1259 | + |
|---|
| 1260 | + list_for_each_entry_safe(dentry, tmp, &root->d_subdirs, d_child) { |
|---|
| 1261 | + if (!simple_positive(dentry)) { |
|---|
| 1262 | + WARN_ON_ONCE(1); /* I think this can't happen? */ |
|---|
| 1263 | + continue; |
|---|
| 1264 | + } |
|---|
| 1265 | + nfsdfs_remove_file(d_inode(root), dentry); |
|---|
| 1266 | + } |
|---|
| 1267 | +} |
|---|
| 1268 | + |
|---|
| 1269 | +/* XXX: cut'n'paste from simple_fill_super; figure out if we could share |
|---|
| 1270 | + * code instead. */ |
|---|
| 1271 | +static int nfsdfs_create_files(struct dentry *root, |
|---|
| 1272 | + const struct tree_descr *files) |
|---|
| 1273 | +{ |
|---|
| 1274 | + struct inode *dir = d_inode(root); |
|---|
| 1275 | + struct inode *inode; |
|---|
| 1276 | + struct dentry *dentry; |
|---|
| 1277 | + int i; |
|---|
| 1278 | + |
|---|
| 1279 | + inode_lock(dir); |
|---|
| 1280 | + for (i = 0; files->name && files->name[0]; i++, files++) { |
|---|
| 1281 | + if (!files->name) |
|---|
| 1282 | + continue; |
|---|
| 1283 | + dentry = d_alloc_name(root, files->name); |
|---|
| 1284 | + if (!dentry) |
|---|
| 1285 | + goto out; |
|---|
| 1286 | + inode = nfsd_get_inode(d_inode(root)->i_sb, |
|---|
| 1287 | + S_IFREG | files->mode); |
|---|
| 1288 | + if (!inode) { |
|---|
| 1289 | + dput(dentry); |
|---|
| 1290 | + goto out; |
|---|
| 1291 | + } |
|---|
| 1292 | + inode->i_fop = files->ops; |
|---|
| 1293 | + inode->i_private = __get_nfsdfs_client(dir); |
|---|
| 1294 | + d_add(dentry, inode); |
|---|
| 1295 | + fsnotify_create(dir, dentry); |
|---|
| 1296 | + } |
|---|
| 1297 | + inode_unlock(dir); |
|---|
| 1298 | + return 0; |
|---|
| 1299 | +out: |
|---|
| 1300 | + nfsdfs_remove_files(root); |
|---|
| 1301 | + inode_unlock(dir); |
|---|
| 1302 | + return -ENOMEM; |
|---|
| 1303 | +} |
|---|
| 1304 | + |
|---|
| 1305 | +/* on success, returns positive number unique to that client. */ |
|---|
| 1306 | +struct dentry *nfsd_client_mkdir(struct nfsd_net *nn, |
|---|
| 1307 | + struct nfsdfs_client *ncl, u32 id, |
|---|
| 1308 | + const struct tree_descr *files) |
|---|
| 1309 | +{ |
|---|
| 1310 | + struct dentry *dentry; |
|---|
| 1311 | + char name[11]; |
|---|
| 1312 | + int ret; |
|---|
| 1313 | + |
|---|
| 1314 | + sprintf(name, "%u", id); |
|---|
| 1315 | + |
|---|
| 1316 | + dentry = nfsd_mkdir(nn->nfsd_client_dir, ncl, name); |
|---|
| 1317 | + if (IS_ERR(dentry)) /* XXX: tossing errors? */ |
|---|
| 1318 | + return NULL; |
|---|
| 1319 | + ret = nfsdfs_create_files(dentry, files); |
|---|
| 1320 | + if (ret) { |
|---|
| 1321 | + nfsd_client_rmdir(dentry); |
|---|
| 1322 | + return NULL; |
|---|
| 1323 | + } |
|---|
| 1324 | + return dentry; |
|---|
| 1325 | +} |
|---|
| 1326 | + |
|---|
| 1327 | +/* Taken from __rpc_rmdir: */ |
|---|
| 1328 | +void nfsd_client_rmdir(struct dentry *dentry) |
|---|
| 1329 | +{ |
|---|
| 1330 | + struct inode *dir = d_inode(dentry->d_parent); |
|---|
| 1331 | + struct inode *inode = d_inode(dentry); |
|---|
| 1332 | + int ret; |
|---|
| 1333 | + |
|---|
| 1334 | + inode_lock(dir); |
|---|
| 1335 | + nfsdfs_remove_files(dentry); |
|---|
| 1336 | + clear_ncl(inode); |
|---|
| 1337 | + dget(dentry); |
|---|
| 1338 | + ret = simple_rmdir(dir, dentry); |
|---|
| 1339 | + WARN_ON_ONCE(ret); |
|---|
| 1340 | + d_drop(dentry); |
|---|
| 1341 | + fsnotify_rmdir(dir, dentry); |
|---|
| 1342 | + dput(dentry); |
|---|
| 1343 | + inode_unlock(dir); |
|---|
| 1344 | +} |
|---|
| 1345 | + |
|---|
| 1346 | +static int nfsd_fill_super(struct super_block *sb, struct fs_context *fc) |
|---|
| 1347 | +{ |
|---|
| 1348 | + struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, |
|---|
| 1349 | + nfsd_net_id); |
|---|
| 1350 | + struct dentry *dentry; |
|---|
| 1351 | + int ret; |
|---|
| 1352 | + |
|---|
| 1154 | 1353 | static const struct tree_descr nfsd_files[] = { |
|---|
| 1155 | 1354 | [NFSD_List] = {"exports", &exports_nfsd_operations, S_IRUGO}, |
|---|
| 1156 | 1355 | [NFSD_Export_features] = {"export_features", |
|---|
| .. | .. |
|---|
| 1179 | 1378 | #endif |
|---|
| 1180 | 1379 | /* last one */ {""} |
|---|
| 1181 | 1380 | }; |
|---|
| 1182 | | - get_net(sb->s_fs_info); |
|---|
| 1183 | | - return simple_fill_super(sb, 0x6e667364, nfsd_files); |
|---|
| 1381 | + |
|---|
| 1382 | + ret = simple_fill_super(sb, 0x6e667364, nfsd_files); |
|---|
| 1383 | + if (ret) |
|---|
| 1384 | + return ret; |
|---|
| 1385 | + dentry = nfsd_mkdir(sb->s_root, NULL, "clients"); |
|---|
| 1386 | + if (IS_ERR(dentry)) |
|---|
| 1387 | + return PTR_ERR(dentry); |
|---|
| 1388 | + nn->nfsd_client_dir = dentry; |
|---|
| 1389 | + return 0; |
|---|
| 1184 | 1390 | } |
|---|
| 1185 | 1391 | |
|---|
| 1186 | | -static struct dentry *nfsd_mount(struct file_system_type *fs_type, |
|---|
| 1187 | | - int flags, const char *dev_name, void *data) |
|---|
| 1392 | +static int nfsd_fs_get_tree(struct fs_context *fc) |
|---|
| 1188 | 1393 | { |
|---|
| 1189 | | - struct net *net = current->nsproxy->net_ns; |
|---|
| 1190 | | - return mount_ns(fs_type, flags, data, net, net->user_ns, nfsd_fill_super); |
|---|
| 1394 | + return get_tree_keyed(fc, nfsd_fill_super, get_net(fc->net_ns)); |
|---|
| 1395 | +} |
|---|
| 1396 | + |
|---|
| 1397 | +static void nfsd_fs_free_fc(struct fs_context *fc) |
|---|
| 1398 | +{ |
|---|
| 1399 | + if (fc->s_fs_info) |
|---|
| 1400 | + put_net(fc->s_fs_info); |
|---|
| 1401 | +} |
|---|
| 1402 | + |
|---|
| 1403 | +static const struct fs_context_operations nfsd_fs_context_ops = { |
|---|
| 1404 | + .free = nfsd_fs_free_fc, |
|---|
| 1405 | + .get_tree = nfsd_fs_get_tree, |
|---|
| 1406 | +}; |
|---|
| 1407 | + |
|---|
| 1408 | +static int nfsd_init_fs_context(struct fs_context *fc) |
|---|
| 1409 | +{ |
|---|
| 1410 | + put_user_ns(fc->user_ns); |
|---|
| 1411 | + fc->user_ns = get_user_ns(fc->net_ns->user_ns); |
|---|
| 1412 | + fc->ops = &nfsd_fs_context_ops; |
|---|
| 1413 | + return 0; |
|---|
| 1191 | 1414 | } |
|---|
| 1192 | 1415 | |
|---|
| 1193 | 1416 | static void nfsd_umount(struct super_block *sb) |
|---|
| 1194 | 1417 | { |
|---|
| 1195 | 1418 | struct net *net = sb->s_fs_info; |
|---|
| 1419 | + |
|---|
| 1420 | + nfsd_shutdown_threads(net); |
|---|
| 1196 | 1421 | |
|---|
| 1197 | 1422 | kill_litter_super(sb); |
|---|
| 1198 | 1423 | put_net(net); |
|---|
| .. | .. |
|---|
| 1201 | 1426 | static struct file_system_type nfsd_fs_type = { |
|---|
| 1202 | 1427 | .owner = THIS_MODULE, |
|---|
| 1203 | 1428 | .name = "nfsd", |
|---|
| 1204 | | - .mount = nfsd_mount, |
|---|
| 1429 | + .init_fs_context = nfsd_init_fs_context, |
|---|
| 1205 | 1430 | .kill_sb = nfsd_umount, |
|---|
| 1206 | 1431 | }; |
|---|
| 1207 | 1432 | MODULE_ALIAS_FS("nfsd"); |
|---|
| .. | .. |
|---|
| 1214 | 1439 | entry = proc_mkdir("fs/nfs", NULL); |
|---|
| 1215 | 1440 | if (!entry) |
|---|
| 1216 | 1441 | return -ENOMEM; |
|---|
| 1217 | | - entry = proc_create("exports", 0, entry, |
|---|
| 1218 | | - &exports_proc_operations); |
|---|
| 1442 | + entry = proc_create("exports", 0, entry, &exports_proc_ops); |
|---|
| 1219 | 1443 | if (!entry) { |
|---|
| 1220 | 1444 | remove_proc_entry("fs/nfs", NULL); |
|---|
| 1221 | 1445 | return -ENOMEM; |
|---|
| .. | .. |
|---|
| 1242 | 1466 | retval = nfsd_idmap_init(net); |
|---|
| 1243 | 1467 | if (retval) |
|---|
| 1244 | 1468 | goto out_idmap_error; |
|---|
| 1469 | + nn->nfsd_versions = NULL; |
|---|
| 1470 | + nn->nfsd4_minorversions = NULL; |
|---|
| 1471 | + retval = nfsd_reply_cache_init(nn); |
|---|
| 1472 | + if (retval) |
|---|
| 1473 | + goto out_drc_error; |
|---|
| 1245 | 1474 | nn->nfsd4_lease = 90; /* default lease time */ |
|---|
| 1246 | 1475 | nn->nfsd4_grace = 90; |
|---|
| 1247 | 1476 | nn->somebody_reclaimed = false; |
|---|
| 1477 | + nn->track_reclaim_completes = false; |
|---|
| 1248 | 1478 | nn->clverifier_counter = prandom_u32(); |
|---|
| 1249 | | - nn->clientid_counter = prandom_u32(); |
|---|
| 1479 | + nn->clientid_base = prandom_u32(); |
|---|
| 1480 | + nn->clientid_counter = nn->clientid_base + 1; |
|---|
| 1481 | + nn->s2s_cp_cl_id = nn->clientid_counter++; |
|---|
| 1250 | 1482 | |
|---|
| 1251 | 1483 | atomic_set(&nn->ntf_refcnt, 0); |
|---|
| 1252 | 1484 | init_waitqueue_head(&nn->ntf_wq); |
|---|
| 1485 | + seqlock_init(&nn->boot_lock); |
|---|
| 1486 | + |
|---|
| 1253 | 1487 | return 0; |
|---|
| 1254 | 1488 | |
|---|
| 1489 | +out_drc_error: |
|---|
| 1490 | + nfsd_idmap_shutdown(net); |
|---|
| 1255 | 1491 | out_idmap_error: |
|---|
| 1256 | 1492 | nfsd_export_shutdown(net); |
|---|
| 1257 | 1493 | out_export_error: |
|---|
| .. | .. |
|---|
| 1260 | 1496 | |
|---|
| 1261 | 1497 | static __net_exit void nfsd_exit_net(struct net *net) |
|---|
| 1262 | 1498 | { |
|---|
| 1499 | + struct nfsd_net *nn = net_generic(net, nfsd_net_id); |
|---|
| 1500 | + |
|---|
| 1501 | + nfsd_reply_cache_shutdown(nn); |
|---|
| 1263 | 1502 | nfsd_idmap_shutdown(net); |
|---|
| 1264 | 1503 | nfsd_export_shutdown(net); |
|---|
| 1504 | + nfsd_netns_free_versions(net_generic(net, nfsd_net_id)); |
|---|
| 1265 | 1505 | } |
|---|
| 1266 | 1506 | |
|---|
| 1267 | 1507 | static struct pernet_operations nfsd_net_ops = { |
|---|
| .. | .. |
|---|
| 1276 | 1516 | int retval; |
|---|
| 1277 | 1517 | printk(KERN_INFO "Installing knfsd (copyright (C) 1996 okir@monad.swb.de).\n"); |
|---|
| 1278 | 1518 | |
|---|
| 1279 | | - retval = register_pernet_subsys(&nfsd_net_ops); |
|---|
| 1280 | | - if (retval < 0) |
|---|
| 1281 | | - return retval; |
|---|
| 1282 | | - retval = register_cld_notifier(); |
|---|
| 1283 | | - if (retval) |
|---|
| 1284 | | - goto out_unregister_pernet; |
|---|
| 1285 | 1519 | retval = nfsd4_init_slabs(); |
|---|
| 1286 | 1520 | if (retval) |
|---|
| 1287 | | - goto out_unregister_notifier; |
|---|
| 1521 | + return retval; |
|---|
| 1288 | 1522 | retval = nfsd4_init_pnfs(); |
|---|
| 1289 | 1523 | if (retval) |
|---|
| 1290 | 1524 | goto out_free_slabs; |
|---|
| 1291 | | - retval = nfsd_fault_inject_init(); /* nfsd fault injection controls */ |
|---|
| 1292 | | - if (retval) |
|---|
| 1293 | | - goto out_exit_pnfs; |
|---|
| 1294 | 1525 | nfsd_stat_init(); /* Statistics */ |
|---|
| 1295 | | - retval = nfsd_reply_cache_init(); |
|---|
| 1526 | + retval = nfsd_drc_slab_create(); |
|---|
| 1296 | 1527 | if (retval) |
|---|
| 1297 | 1528 | goto out_free_stat; |
|---|
| 1298 | 1529 | nfsd_lockd_init(); /* lockd->nfsd callbacks */ |
|---|
| .. | .. |
|---|
| 1301 | 1532 | goto out_free_lockd; |
|---|
| 1302 | 1533 | retval = register_filesystem(&nfsd_fs_type); |
|---|
| 1303 | 1534 | if (retval) |
|---|
| 1535 | + goto out_free_exports; |
|---|
| 1536 | + retval = register_pernet_subsys(&nfsd_net_ops); |
|---|
| 1537 | + if (retval < 0) |
|---|
| 1538 | + goto out_free_filesystem; |
|---|
| 1539 | + retval = register_cld_notifier(); |
|---|
| 1540 | + if (retval) |
|---|
| 1304 | 1541 | goto out_free_all; |
|---|
| 1305 | 1542 | return 0; |
|---|
| 1306 | 1543 | out_free_all: |
|---|
| 1544 | + unregister_pernet_subsys(&nfsd_net_ops); |
|---|
| 1545 | +out_free_filesystem: |
|---|
| 1546 | + unregister_filesystem(&nfsd_fs_type); |
|---|
| 1547 | +out_free_exports: |
|---|
| 1307 | 1548 | remove_proc_entry("fs/nfs/exports", NULL); |
|---|
| 1308 | 1549 | remove_proc_entry("fs/nfs", NULL); |
|---|
| 1309 | 1550 | out_free_lockd: |
|---|
| 1310 | 1551 | nfsd_lockd_shutdown(); |
|---|
| 1311 | | - nfsd_reply_cache_shutdown(); |
|---|
| 1552 | + nfsd_drc_slab_free(); |
|---|
| 1312 | 1553 | out_free_stat: |
|---|
| 1313 | 1554 | nfsd_stat_shutdown(); |
|---|
| 1314 | | - nfsd_fault_inject_cleanup(); |
|---|
| 1315 | | -out_exit_pnfs: |
|---|
| 1316 | 1555 | nfsd4_exit_pnfs(); |
|---|
| 1317 | 1556 | out_free_slabs: |
|---|
| 1318 | 1557 | nfsd4_free_slabs(); |
|---|
| 1319 | | -out_unregister_notifier: |
|---|
| 1320 | | - unregister_cld_notifier(); |
|---|
| 1321 | | -out_unregister_pernet: |
|---|
| 1322 | | - unregister_pernet_subsys(&nfsd_net_ops); |
|---|
| 1323 | 1558 | return retval; |
|---|
| 1324 | 1559 | } |
|---|
| 1325 | 1560 | |
|---|
| 1326 | 1561 | static void __exit exit_nfsd(void) |
|---|
| 1327 | 1562 | { |
|---|
| 1328 | | - nfsd_reply_cache_shutdown(); |
|---|
| 1563 | + unregister_cld_notifier(); |
|---|
| 1564 | + unregister_pernet_subsys(&nfsd_net_ops); |
|---|
| 1565 | + nfsd_drc_slab_free(); |
|---|
| 1329 | 1566 | remove_proc_entry("fs/nfs/exports", NULL); |
|---|
| 1330 | 1567 | remove_proc_entry("fs/nfs", NULL); |
|---|
| 1331 | 1568 | nfsd_stat_shutdown(); |
|---|
| 1332 | 1569 | nfsd_lockd_shutdown(); |
|---|
| 1333 | 1570 | nfsd4_free_slabs(); |
|---|
| 1334 | 1571 | nfsd4_exit_pnfs(); |
|---|
| 1335 | | - nfsd_fault_inject_cleanup(); |
|---|
| 1336 | 1572 | unregister_filesystem(&nfsd_fs_type); |
|---|
| 1337 | | - unregister_cld_notifier(); |
|---|
| 1338 | | - unregister_pernet_subsys(&nfsd_net_ops); |
|---|
| 1339 | 1573 | } |
|---|
| 1340 | 1574 | |
|---|
| 1341 | 1575 | MODULE_AUTHOR("Olaf Kirch <okir@monad.swb.de>"); |
|---|
| 1342 | 1576 | MODULE_LICENSE("GPL"); |
|---|
| 1577 | +MODULE_IMPORT_NS(ANDROID_GKI_VFS_EXPORT_ONLY); |
|---|
| 1343 | 1578 | module_init(init_nfsd) |
|---|
| 1344 | 1579 | module_exit(exit_nfsd) |
|---|