📄 vfs.c
字号:
nfsd_readlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *buf, int *lenp){ struct dentry *dentry; struct inode *inode; mm_segment_t oldfs; int err; err = fh_verify(rqstp, fhp, S_IFLNK, MAY_NOP); if (err) goto out; dentry = fhp->fh_dentry; inode = dentry->d_inode; err = nfserr_inval; if (!inode->i_op || !inode->i_op->readlink) goto out; UPDATE_ATIME(inode); /* N.B. Why does this call need a get_fs()?? * Remove the set_fs and watch the fireworks:-) --okir */ oldfs = get_fs(); set_fs(KERNEL_DS); err = inode->i_op->readlink(dentry, buf, *lenp); set_fs(oldfs); if (err < 0) goto out_nfserr; *lenp = err; err = 0;out: return err;out_nfserr: err = nfserrno(err); goto out;}/* * Create a symlink and look up its inode * N.B. After this call _both_ fhp and resfhp need an fh_put */intnfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp, char *fname, int flen, char *path, int plen, struct svc_fh *resfhp, struct iattr *iap){ struct dentry *dentry, *dnew; int err, cerr; err = nfserr_noent; if (!flen || !plen) goto out; err = nfserr_exist; if (isdotent(fname, flen)) goto out; err = fh_verify(rqstp, fhp, S_IFDIR, MAY_CREATE); if (err) goto out; fh_lock(fhp); dentry = fhp->fh_dentry; dnew = lookup_one_len(fname, dentry, flen); err = PTR_ERR(dnew); if (IS_ERR(dnew)) goto out_nfserr; err = vfs_symlink(dentry->d_inode, dnew, path); if (!err) { if (EX_ISSYNC(fhp->fh_export)) nfsd_sync_dir(dentry); if (iap) { iap->ia_valid &= ATTR_MODE /* ~(ATTR_MODE|ATTR_UID|ATTR_GID)*/; if (iap->ia_valid) { iap->ia_valid |= ATTR_CTIME; iap->ia_mode = (iap->ia_mode&S_IALLUGO) | S_IFLNK; err = notify_change(dnew, iap); if (!err && EX_ISSYNC(fhp->fh_export)) write_inode_now(dentry->d_inode, 1); } } } else err = nfserrno(err); fh_unlock(fhp); /* Compose the fh so the dentry will be freed ... */ cerr = fh_compose(resfhp, fhp->fh_export, dnew, fhp); if (err==0) err = cerr;out: return err;out_nfserr: err = nfserrno(err); goto out;}/* * Create a hardlink * N.B. After this call _both_ ffhp and tfhp need an fh_put */intnfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *name, int len, struct svc_fh *tfhp){ struct dentry *ddir, *dnew, *dold; struct inode *dirp, *dest; int err; err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_CREATE); if (err) goto out; err = fh_verify(rqstp, tfhp, -S_IFDIR, MAY_NOP); if (err) goto out; err = nfserr_perm; if (!len) goto out; err = nfserr_exist; if (isdotent(name, len)) goto out; fh_lock(ffhp); ddir = ffhp->fh_dentry; dirp = ddir->d_inode; dnew = lookup_one_len(name, ddir, len); err = PTR_ERR(dnew); if (IS_ERR(dnew)) goto out_nfserr; dold = tfhp->fh_dentry; dest = dold->d_inode; err = vfs_link(dold, dirp, dnew); if (!err) { if (EX_ISSYNC(ffhp->fh_export)) { nfsd_sync_dir(ddir); write_inode_now(dest, 1); } } else { if (err == -EXDEV && rqstp->rq_vers == 2) err = nfserr_acces; else err = nfserrno(err); } fh_unlock(ffhp); dput(dnew);out: return err;out_nfserr: err = nfserrno(err); goto out;}/* * Rename a file * N.B. After this call _both_ ffhp and tfhp need an fh_put */intnfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen, struct svc_fh *tfhp, char *tname, int tlen){ struct dentry *fdentry, *tdentry, *odentry, *ndentry; struct inode *fdir, *tdir; int err; err = fh_verify(rqstp, ffhp, S_IFDIR, MAY_REMOVE); if (err) goto out; err = fh_verify(rqstp, tfhp, S_IFDIR, MAY_CREATE); if (err) goto out; fdentry = ffhp->fh_dentry; fdir = fdentry->d_inode; tdentry = tfhp->fh_dentry; tdir = tdentry->d_inode; err = (rqstp->rq_vers == 2) ? nfserr_acces : nfserr_xdev; if (fdir->i_dev != tdir->i_dev) goto out; err = nfserr_perm; if (!flen || isdotent(fname, flen) || !tlen || isdotent(tname, tlen)) goto out; /* cannot use fh_lock as we need deadlock protective ordering * so do it by hand */ double_down(&tdir->i_sem, &fdir->i_sem); ffhp->fh_locked = tfhp->fh_locked = 1; fill_pre_wcc(ffhp); fill_pre_wcc(tfhp); odentry = lookup_one_len(fname, fdentry, flen); err = PTR_ERR(odentry); if (IS_ERR(odentry)) goto out_nfserr; err = -ENOENT; if (!odentry->d_inode) goto out_dput_old; ndentry = lookup_one_len(tname, tdentry, tlen); err = PTR_ERR(ndentry); if (IS_ERR(ndentry)) goto out_dput_old;#ifdef MSNFS if ((ffhp->fh_export->ex_flags & NFSEXP_MSNFS) && ((atomic_read(&odentry->d_count) > 1) || (atomic_read(&ndentry->d_count) > 1))) { err = nfserr_perm; } else#endif err = vfs_rename(fdir, odentry, tdir, ndentry); if (!err && EX_ISSYNC(tfhp->fh_export)) { nfsd_sync_dir(tdentry); nfsd_sync_dir(fdentry); } dput(ndentry); out_dput_old: dput(odentry); out_nfserr: if (err) err = nfserrno(err); /* we cannot reply on fh_unlock on the two filehandles, * as that would do the wrong thing if the two directories * were the same, so again we do it by hand */ fill_post_wcc(ffhp); fill_post_wcc(tfhp); double_up(&tdir->i_sem, &fdir->i_sem); ffhp->fh_locked = tfhp->fh_locked = 0; out: return err;}/* * Unlink a file or directory * N.B. After this call fhp needs an fh_put */intnfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, char *fname, int flen){ struct dentry *dentry, *rdentry; struct inode *dirp; int err; err = nfserr_acces; if (!flen || isdotent(fname, flen)) goto out; err = fh_verify(rqstp, fhp, S_IFDIR, MAY_REMOVE); if (err) goto out; fh_lock(fhp); dentry = fhp->fh_dentry; dirp = dentry->d_inode; rdentry = lookup_one_len(fname, dentry, flen); err = PTR_ERR(rdentry); if (IS_ERR(rdentry)) goto out_nfserr; if (!rdentry->d_inode) { dput(rdentry); err = nfserr_noent; goto out; } if (type != S_IFDIR) { /* It's UNLINK */#ifdef MSNFS if ((fhp->fh_export->ex_flags & NFSEXP_MSNFS) && (atomic_read(&rdentry->d_count) > 1)) { err = nfserr_perm; } else#endif err = vfs_unlink(dirp, rdentry); } else { /* It's RMDIR */ err = vfs_rmdir(dirp, rdentry); } dput(rdentry); if (err) goto out_nfserr; if (EX_ISSYNC(fhp->fh_export)) nfsd_sync_dir(dentry);out: return err;out_nfserr: err = nfserrno(err); goto out;}/* * Read entries from a directory. * The verifier is an NFSv3 thing we ignore for now. */intnfsd_readdir(struct svc_rqst *rqstp, struct svc_fh *fhp, loff_t offset, encode_dent_fn func, u32 *buffer, int *countp, u32 *verf){ struct inode *inode; u32 *p; int oldlen, eof, err; struct file file; struct readdir_cd cd; err = nfsd_open(rqstp, fhp, S_IFDIR, MAY_READ, &file); if (err) goto out; if (offset > ~(u32) 0) goto out_close; err = nfserr_notdir; if (!file.f_op->readdir) goto out_close; file.f_pos = offset; /* Set up the readdir context */ memset(&cd, 0, sizeof(cd)); cd.rqstp = rqstp; cd.buffer = buffer; cd.buflen = *countp; /* count of words */ cd.dirfh = fhp; /* * Read the directory entries. This silly loop is necessary because * readdir() is not guaranteed to fill up the entire buffer, but * may choose to do less. */ inode = file.f_dentry->d_inode; down(&inode->i_sem); while (1) { oldlen = cd.buflen; /* dprintk("nfsd: f_op->readdir(%x/%ld @ %d) buflen = %d (%d)\n", file.f_inode->i_dev, file.f_inode->i_ino, (int) file.f_pos, (int) oldlen, (int) cd.buflen); */ err = file.f_op->readdir(&file, &cd, (filldir_t) func); if (err < 0) goto out_nfserr; if (oldlen == cd.buflen) break; if (cd.eob) break; } up(&inode->i_sem); /* If we didn't fill the buffer completely, we're at EOF */ eof = !cd.eob; if (cd.offset) { if (rqstp->rq_vers == 3) (void)xdr_encode_hyper(cd.offset, file.f_pos); else *cd.offset = htonl(file.f_pos); } p = cd.buffer; *p++ = 0; /* no more entries */ *p++ = htonl(eof); /* end of directory */ *countp = (caddr_t) p - (caddr_t) buffer; dprintk("nfsd: readdir result %d bytes, eof %d offset %d\n", *countp, eof, cd.offset? ntohl(*cd.offset) : -1); err = 0;out_close: nfsd_close(&file);out: return err;out_nfserr: up(&inode->i_sem); err = nfserrno(err); goto out_close;}/* * Get file system stats * N.B. After this call fhp needs an fh_put */intnfsd_statfs(struct svc_rqst *rqstp, struct svc_fh *fhp, struct statfs *stat){ int err = fh_verify(rqstp, fhp, 0, MAY_NOP); if (!err && vfs_statfs(fhp->fh_dentry->d_inode->i_sb,stat)) err = nfserr_io; return err;}/* * Check for a user's access permissions to this inode. */intnfsd_permission(struct svc_export *exp, struct dentry *dentry, int acc){ struct inode *inode = dentry->d_inode; int err; if (acc == MAY_NOP) return 0;#if 0 dprintk("nfsd: permission 0x%x%s%s%s%s%s%s%s mode 0%o%s%s%s\n", acc, (acc & MAY_READ)? " read" : "", (acc & MAY_WRITE)? " write" : "", (acc & MAY_EXEC)? " exec" : "", (acc & MAY_SATTR)? " sattr" : "", (acc & MAY_TRUNC)? " trunc" : "", (acc & MAY_LOCK)? " lock" : "", (acc & MAY_OWNER_OVERRIDE)? " owneroverride" : "", inode->i_mode, IS_IMMUTABLE(inode)? " immut" : "", IS_APPEND(inode)? " append" : "", IS_RDONLY(inode)? " ro" : ""); dprintk(" owner %d/%d user %d/%d\n", inode->i_uid, inode->i_gid, current->fsuid, current->fsgid);#endif /* only care about readonly exports for files and * directories. links don't have meaningful write access, * and all else is local to the client */ if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)) if (acc & (MAY_WRITE | MAY_SATTR | MAY_TRUNC)) { if (EX_RDONLY(exp) || IS_RDONLY(inode)) return nfserr_rofs; if (/* (acc & MAY_WRITE) && */ IS_IMMUTABLE(inode)) return nfserr_perm; } if ((acc & MAY_TRUNC) && IS_APPEND(inode)) return nfserr_perm; if (acc & MAY_LOCK) { /* If we cannot rely on authentication in NLM requests, * just allow locks, otherwise require read permission, or * ownership */ if (exp->ex_flags & NFSEXP_NOAUTHNLM) return 0; else acc = MAY_READ | MAY_OWNER_OVERRIDE; } /* * The file owner always gets access permission for accesses that * would normally be checked at open time. This is to make * file access work even when the client has done a fchmod(fd, 0). * * However, `cp foo bar' should fail nevertheless when bar is * readonly. A sensible way to do this might be to reject all * attempts to truncate a read-only file, because a creat() call * always implies file truncation. * ... but this isn't really fair. A process may reasonably call * ftruncate on an open file descriptor on a file with perm 000. * We must trust the client to do permission checking - using "ACCESS" * with NFSv3. */ if ((acc & MAY_OWNER_OVERRIDE) && inode->i_uid == current->fsuid) return 0; acc &= ~ MAY_OWNER_OVERRIDE; /* This bit is no longer needed, and gets in the way later */ err = permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC)); /* Allow read access to binaries even when mode 111 */ if (err == -EACCES && S_ISREG(inode->i_mode) && acc == MAY_READ) err = permission(inode, MAY_EXEC); return err? nfserrno(err) : 0;}voidnfsd_racache_shutdown(void){ if (!raparm_cache) return; dprintk("nfsd: freeing readahead buffers.\n"); kfree(raparml); raparm_cache = raparml = NULL;}/* * Initialize readahead param cache */intnfsd_racache_init(int cache_size){ int i; if (raparm_cache) return 0; raparml = kmalloc(sizeof(struct raparms) * cache_size, GFP_KERNEL); if (raparml != NULL) { dprintk("nfsd: allocating %d readahead buffers.\n", cache_size); memset(raparml, 0, sizeof(struct raparms) * cache_size); for (i = 0; i < cache_size - 1; i++) { raparml[i].p_next = raparml + i + 1; } raparm_cache = raparml; } else { printk(KERN_WARNING "nfsd: Could not allocate memory read-ahead cache.\n"); return -ENOMEM; } nfsdstats.ra_size = cache_size; return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -