/* * Note: we hold the dentry use count while the file is open. */ static __be32 nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) { __be32 nfserr; struct svc_fh fh; /* must initialize before using! but maxsize doesn't matter */ fh_init(&fh,0); fh.fh_handle.fh_size = f->size; memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size); fh.fh_export = NULL; exp_readlock(); nfserr = nfsd_open(rqstp, &fh, S_IFREG, NFSD_MAY_LOCK, filp); fh_put(&fh); exp_readunlock(); /* We return nlm error codes as nlm doesn't know * about nfsd, but nfsd does know about nlm.. */ switch (nfserr) { case nfs_ok: return 0; case nfserr_dropit: return nlm_drop_reply; case nfserr_stale: return nlm_stale_fh; default: return nlm_failed; } }
static ssize_t write_getfs(struct file *file, char *buf, size_t size) { struct nfsctl_fsparm *data; struct sockaddr_in *sin; struct auth_domain *clp; int err = 0; struct knfsd_fh *res; if (size < sizeof(*data)) return -EINVAL; data = (struct nfsctl_fsparm*)buf; err = -EPROTONOSUPPORT; if (data->gd_addr.sa_family != AF_INET) goto out; sin = (struct sockaddr_in *)&data->gd_addr; if (data->gd_maxlen > NFS3_FHSIZE) data->gd_maxlen = NFS3_FHSIZE; res = (struct knfsd_fh*)buf; exp_readlock(); if (!(clp = auth_unix_lookup(sin->sin_addr))) err = -EPERM; else { err = exp_rootfh(clp, data->gd_path, res, data->gd_maxlen); auth_domain_put(clp); } exp_readunlock(); if (err == 0) err = res->fh_size + (int)&((struct knfsd_fh*)0)->fh_base; out: return err; }
static inline int nfsctl_getfh(struct nfsctl_fhparm *data, __u8 *res) { struct sockaddr_in *sin; struct svc_client *clp; int err = 0; struct knfsd_fh fh; if (data->gf_addr.sa_family != AF_INET) return -EPROTONOSUPPORT; if (data->gf_version < 2 || data->gf_version > NFSSVC_MAXVERS) return -EINVAL; sin = (struct sockaddr_in *)&data->gf_addr; exp_readlock(); if (!(clp = exp_getclient(sin))) err = -EPERM; else err = exp_rootfh(clp, to_kdev_t(data->gf_dev), data->gf_ino, NULL, &fh, NFS_FHSIZE); exp_unlock(); if (err == 0) { if (fh.fh_size > NFS_FHSIZE) err = -EINVAL; else { memset(res,0, NFS_FHSIZE); memcpy(res, &fh.fh_base, fh.fh_size); } } return err; }
/* * Note: we hold the dentry use count while the file is open. */ static u32 nlm_fopen(struct svc_rqst *rqstp, struct nfs_fh *f, struct file **filp) { u32 nfserr; struct svc_fh fh; /* must initialize before using! but maxsize doesn't matter */ fh_init(&fh,0); fh.fh_handle.fh_size = f->size; memcpy((char*)&fh.fh_handle.fh_base, f->data, f->size); fh.fh_export = NULL; exp_readlock(); nfserr = nfsd_open(rqstp, &fh, S_IFREG, MAY_LOCK, filp); fh_put(&fh); rqstp->rq_client = NULL; exp_readunlock(); /* nlm and nfsd don't share error codes. * we invent: 0 = no error * 1 = stale file handle * 2 = nfserr_dropit (or -EAGAIN) * 3 = other error */ switch (nfserr) { case nfs_ok: return 0; case nfserr_stale: return 1; case nfserr_dropit: return 2; default: return 3; } }
/** * write_getfd - Get a fixed-length NFS file handle by path (used by mountd) * * Deprecated. /proc/fs/nfsd/filehandle is preferred. * Function remains to support old versions of nfs-utils. * * Input: * buf: struct nfsctl_fdparm * gd_addr: socket address of client * gd_path: '\0'-terminated C string * containing pathname of * directory in local file system * gd_version: fdparm structure version * size: size in bytes of passed in nfsctl_fdparm * Output: * On success: passed-in buffer filled with nfsctl_res * (a fixed-length raw NFS file handle); * return code is the size in bytes of the file handle * On error: return code is negative errno value * * Note: Only AF_INET client addresses are passed in, since gd_addr * is the same size as a struct sockaddr_in. */ static ssize_t write_getfd(struct file *file, char *buf, size_t size) { struct nfsctl_fdparm *data; struct sockaddr_in *sin; struct auth_domain *clp; int err = 0; struct knfsd_fh fh; char *res; struct in6_addr in6; if (size < sizeof(*data)) return -EINVAL; data = (struct nfsctl_fdparm*)buf; err = -EPROTONOSUPPORT; if (data->gd_addr.sa_family != AF_INET) goto out; err = -EINVAL; if (data->gd_version < 2 || data->gd_version > NFSSVC_MAXVERS) goto out; res = buf; sin = (struct sockaddr_in *)&data->gd_addr; exp_readlock(); ipv6_addr_set_v4mapped(sin->sin_addr.s_addr, &in6); clp = auth_unix_lookup(&init_net, &in6); if (!clp) err = -EPERM; else { err = exp_rootfh(clp, data->gd_path, &fh, NFS_FHSIZE); auth_domain_put(clp); } exp_readunlock(); if (err == 0) { memset(res,0, NFS_FHSIZE); memcpy(res, &fh.fh_base, fh.fh_size); err = NFS_FHSIZE; } out: return err; }
static inline int nfsctl_getfs(struct nfsctl_fsparm *data, struct knfsd_fh *res) { struct sockaddr_in *sin; struct svc_client *clp; int err = 0; if (data->gd_addr.sa_family != AF_INET) return -EPROTONOSUPPORT; sin = (struct sockaddr_in *)&data->gd_addr; if (data->gd_maxlen > NFS3_FHSIZE) data->gd_maxlen = NFS3_FHSIZE; exp_readlock(); if (!(clp = exp_getclient(sin))) err = -EPERM; else err = exp_rootfh(clp, 0, 0, data->gd_path, res, data->gd_maxlen); exp_unlock(); return err; }
/* * This is the NFS server kernel thread */ static void nfsd(struct svc_rqst *rqstp) { struct svc_serv *serv = rqstp->rq_server; struct fs_struct *fsp; int err; struct nfsd_list me; sigset_t shutdown_mask, allowed_mask; /* Lock module and set up kernel thread */ lock_kernel(); daemonize("nfsd"); current->rlim[RLIMIT_FSIZE].rlim_cur = RLIM_INFINITY; /* After daemonize() this kernel thread shares current->fs * with the init process. We need to create files with a * umask of 0 instead of init's umask. */ fsp = copy_fs_struct(current->fs); if (!fsp) { printk("Unable to start nfsd thread: out of memory\n"); goto out; } exit_fs(current); current->fs = fsp; current->fs->umask = 0; siginitsetinv(&shutdown_mask, SHUTDOWN_SIGS); siginitsetinv(&allowed_mask, ALLOWED_SIGS); nfsdstats.th_cnt++; lockd_up(); /* start lockd */ me.task = current; list_add(&me.list, &nfsd_list); unlock_kernel(); /* * We want less throttling in balance_dirty_pages() so that nfs to * localhost doesn't cause nfsd to lock up due to all the client's * dirty pages. */ current->flags |= PF_LESS_THROTTLE; /* * The main request loop */ for (;;) { /* Block all but the shutdown signals */ sigprocmask(SIG_SETMASK, &shutdown_mask, NULL); /* * Find a socket with data available and call its * recvfrom routine. */ while ((err = svc_recv(serv, rqstp, 60*60*HZ)) == -EAGAIN) ; if (err < 0) break; update_thread_usage(atomic_read(&nfsd_busy)); atomic_inc(&nfsd_busy); /* Lock the export hash tables for reading. */ exp_readlock(); /* Process request with signals blocked. */ sigprocmask(SIG_SETMASK, &allowed_mask, NULL); svc_process(serv, rqstp); /* Unlock export hash tables */ exp_readunlock(); update_thread_usage(atomic_read(&nfsd_busy)); atomic_dec(&nfsd_busy); } if (err != -EINTR) { printk(KERN_WARNING "nfsd: terminating on error %d\n", -err); } else { unsigned int signo; for (signo = 1; signo <= _NSIG; signo++) if (sigismember(¤t->pending.signal, signo) && !sigismember(¤t->blocked, signo)) break; err = signo; } lock_kernel(); /* Release lockd */ lockd_down(); /* Check if this is last thread */ if (serv->sv_nrthreads==1) { printk(KERN_WARNING "nfsd: last server has exited\n"); if (err != SIG_NOCLEAN) { printk(KERN_WARNING "nfsd: unexporting all filesystems\n"); nfsd_export_flush(); } nfsd_serv = NULL; nfsd_racache_shutdown(); /* release read-ahead cache */ nfs4_state_shutdown(); } list_del(&me.list); nfsdstats.th_cnt --; out: /* Release the thread */ svc_exit_thread(rqstp); /* Release module */ module_put_and_exit(0); }