📄 dir.c
字号:
} /* Open the file on the server */ lock_kernel(); res = nfs4_atomic_open(dir, dentry, nd); unlock_kernel(); if (IS_ERR(res)) { error = PTR_ERR(res); switch (error) { /* Make a negative dentry */ case -ENOENT: res = NULL; goto out; /* This turned out not to be a regular file */ case -EISDIR: case -ENOTDIR: goto no_open; case -ELOOP: if (!(nd->intent.open.flags & O_NOFOLLOW)) goto no_open; /* case -EINVAL: */ default: goto out; } } else if (res != NULL) dentry = res;out: return res;no_open: return nfs_lookup(dir, dentry, nd);}static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd){ struct dentry *parent = NULL; struct inode *inode = dentry->d_inode; struct inode *dir; int openflags, ret = 0; parent = dget_parent(dentry); dir = parent->d_inode; if (!is_atomic_open(dir, nd)) goto no_open; /* We can't create new files in nfs_open_revalidate(), so we * optimize away revalidation of negative dentries. */ if (inode == NULL) { if (!nfs_neg_need_reval(dir, dentry, nd)) ret = 1; goto out; } /* NFS only supports OPEN on regular files */ if (!S_ISREG(inode->i_mode)) goto no_open; openflags = nd->intent.open.flags; /* We cannot do exclusive creation on a positive dentry */ if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL)) goto no_open; /* We can't create new files, or truncate existing ones here */ openflags &= ~(O_CREAT|O_TRUNC); /* * Note: we're not holding inode->i_mutex and so may be racing with * operations that change the directory. We therefore save the * change attribute *before* we do the RPC call. */ lock_kernel(); ret = nfs4_open_revalidate(dir, dentry, openflags, nd); unlock_kernel();out: dput(parent); if (!ret) d_drop(dentry); return ret;no_open: dput(parent); if (inode != NULL && nfs_have_delegation(inode, FMODE_READ)) return 1; return nfs_lookup_revalidate(dentry, nd);}#endif /* CONFIG_NFSV4 */static struct dentry *nfs_readdir_lookup(nfs_readdir_descriptor_t *desc){ struct dentry *parent = desc->file->f_path.dentry; struct inode *dir = parent->d_inode; struct nfs_entry *entry = desc->entry; struct dentry *dentry, *alias; struct qstr name = { .name = entry->name, .len = entry->len, }; struct inode *inode; unsigned long verf = nfs_save_change_attribute(dir); switch (name.len) { case 2: if (name.name[0] == '.' && name.name[1] == '.') return dget_parent(parent); break; case 1: if (name.name[0] == '.') return dget(parent); } spin_lock(&dir->i_lock); if (NFS_I(dir)->cache_validity & NFS_INO_INVALID_DATA) { spin_unlock(&dir->i_lock); return NULL; } spin_unlock(&dir->i_lock); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); if (dentry != NULL) { /* Is this a positive dentry that matches the readdir info? */ if (dentry->d_inode != NULL && (NFS_FILEID(dentry->d_inode) == entry->ino || d_mountpoint(dentry))) { if (!desc->plus || entry->fh->size == 0) return dentry; if (nfs_compare_fh(NFS_FH(dentry->d_inode), entry->fh) == 0) goto out_renew; } /* No, so d_drop to allow one to be created */ d_drop(dentry); dput(dentry); } if (!desc->plus || !(entry->fattr->valid & NFS_ATTR_FATTR)) return NULL; if (name.len > NFS_SERVER(dir)->namelen) return NULL; /* Note: caller is already holding the dir->i_mutex! */ dentry = d_alloc(parent, &name); if (dentry == NULL) return NULL; dentry->d_op = NFS_PROTO(dir)->dentry_ops; inode = nfs_fhget(dentry->d_sb, entry->fh, entry->fattr); if (IS_ERR(inode)) { dput(dentry); return NULL; } alias = d_materialise_unique(dentry, inode); if (alias != NULL) { dput(dentry); if (IS_ERR(alias)) return NULL; dentry = alias; }out_renew: nfs_set_verifier(dentry, verf); return dentry;}/* * Code common to create, mkdir, and mknod. */int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, struct nfs_fattr *fattr){ struct dentry *parent = dget_parent(dentry); struct inode *dir = parent->d_inode; struct inode *inode; int error = -EACCES; d_drop(dentry); /* We may have been initialized further down */ if (dentry->d_inode) goto out; if (fhandle->size == 0) { error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); if (error) goto out_error; } nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); if (!(fattr->valid & NFS_ATTR_FATTR)) { struct nfs_server *server = NFS_SB(dentry->d_sb); error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr); if (error < 0) goto out_error; } inode = nfs_fhget(dentry->d_sb, fhandle, fattr); error = PTR_ERR(inode); if (IS_ERR(inode)) goto out_error; d_add(dentry, inode);out: dput(parent); return 0;out_error: nfs_mark_for_revalidate(dir); dput(parent); return error;}/* * Following a failed create operation, we drop the dentry rather * than retain a negative dentry. This avoids a problem in the event * that the operation succeeded on the server, but an error in the * reply path made it appear to have failed. */static int nfs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *nd){ struct iattr attr; int error; int open_flags = 0; dfprintk(VFS, "NFS: create(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; if ((nd->flags & LOOKUP_CREATE) != 0) open_flags = nd->intent.open.flags; lock_kernel(); error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, nd); if (error != 0) goto out_err; unlock_kernel(); return 0;out_err: unlock_kernel(); d_drop(dentry); return error;}/* * See comments for nfs_proc_create regarding failed operations. */static intnfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev){ struct iattr attr; int status; dfprintk(VFS, "NFS: mknod(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); if (!new_valid_dev(rdev)) return -EINVAL; attr.ia_mode = mode; attr.ia_valid = ATTR_MODE; lock_kernel(); status = NFS_PROTO(dir)->mknod(dir, dentry, &attr, rdev); if (status != 0) goto out_err; unlock_kernel(); return 0;out_err: unlock_kernel(); d_drop(dentry); return status;}/* * See comments for nfs_proc_create regarding failed operations. */static int nfs_mkdir(struct inode *dir, struct dentry *dentry, int mode){ struct iattr attr; int error; dfprintk(VFS, "NFS: mkdir(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); attr.ia_valid = ATTR_MODE; attr.ia_mode = mode | S_IFDIR; lock_kernel(); error = NFS_PROTO(dir)->mkdir(dir, dentry, &attr); if (error != 0) goto out_err; unlock_kernel(); return 0;out_err: d_drop(dentry); unlock_kernel(); return error;}static int nfs_rmdir(struct inode *dir, struct dentry *dentry){ int error; dfprintk(VFS, "NFS: rmdir(%s/%ld), %s\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); lock_kernel(); error = NFS_PROTO(dir)->rmdir(dir, &dentry->d_name); /* Ensure the VFS deletes this inode */ if (error == 0 && dentry->d_inode != NULL) clear_nlink(dentry->d_inode); unlock_kernel(); return error;}static int nfs_sillyrename(struct inode *dir, struct dentry *dentry){ static unsigned int sillycounter; const int fileidsize = sizeof(NFS_FILEID(dentry->d_inode))*2; const int countersize = sizeof(sillycounter)*2; const int slen = sizeof(".nfs")+fileidsize+countersize-1; char silly[slen+1]; struct qstr qsilly; struct dentry *sdentry; int error = -EIO; dfprintk(VFS, "NFS: silly-rename(%s/%s, ct=%d)\n", dentry->d_parent->d_name.name, dentry->d_name.name, atomic_read(&dentry->d_count)); nfs_inc_stats(dir, NFSIOS_SILLYRENAME); /* * We don't allow a dentry to be silly-renamed twice. */ error = -EBUSY; if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto out; sprintf(silly, ".nfs%*.*Lx", fileidsize, fileidsize, (unsigned long long)NFS_FILEID(dentry->d_inode)); /* Return delegation in anticipation of the rename */ nfs_inode_return_delegation(dentry->d_inode); sdentry = NULL; do { char *suffix = silly + slen - countersize; dput(sdentry); sillycounter++; sprintf(suffix, "%*.*x", countersize, countersize, sillycounter); dfprintk(VFS, "NFS: trying to rename %s to %s\n", dentry->d_name.name, silly); sdentry = lookup_one_len(silly, dentry->d_parent, slen); /* * N.B. Better to return EBUSY here ... it could be * dangerous to delete the file while it's in use. */ if (IS_ERR(sdentry)) goto out; } while(sdentry->d_inode != NULL); /* need negative lookup */ qsilly.name = silly; qsilly.len = strlen(silly); if (dentry->d_inode) { error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); nfs_mark_for_revalidate(dentry->d_inode); } else error = NFS_PROTO(dir)->rename(dir, &dentry->d_name, dir, &qsilly); if (!error) { nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); d_move(dentry, sdentry); error = nfs_async_unlink(dir, dentry); /* If we return 0 we don't unlink */ } dput(sdentry);out: return error;}/* * Remove a file after making sure there are no pending writes, * and after checking that the file has only one user. * * We invalidate the attribute cache and free the inode prior to the operation * to avoid possible races if the server reuses the inode. */static int nfs_safe_remove(struct dentry *dentry){ struct inode *dir = dentry->d_parent->d_inode; struct inode *inode = dentry->d_inode; int error = -EBUSY; dfprintk(VFS, "NFS: safe_remove(%s/%s)\n", dentry->d_parent->d_name.name, dentry->d_name.name); /* If the dentry was sillyrenamed, we simply call d_delete() */ if (dentry->d_flags & DCACHE_NFSFS_RENAMED) { error = 0; goto out; } if (inode != NULL) { nfs_inode_return_delegation(inode); error = NFS_PROTO(dir)->remove(dir, &dentry->d_name); /* The VFS may want to delete this inode */ if (error == 0) drop_nlink(inode); nfs_mark_for_revalidate(inode); } else error = NFS_PROTO(dir)->remove(dir, &dentry->d_name);out: return error;}/* We do silly rename. In case sillyrename() returns -EBUSY, the inode * belongs to an active ".nfs..." file and we return -EBUSY. * * If sillyrename() returns 0, we do nothing, otherwise we unlink. */static int nfs_unlink(struct inode *dir, struct dentry *dentry){ int error; int need_rehash = 0; dfprintk(VFS, "NFS: unlink(%s/%ld, %s)\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name); lock_kernel(); spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); if (atomic_read(&dentry->d_count) > 1) { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); /* Start asynchronous writeout of the inode */ write_inode_now(dentry->d_inode, 0); error = nfs_sillyrename(dir, dentry); unlock_kernel(); return error; } if (!d_unhashed(dentry)) { __d_drop(dentry); need_rehash = 1; } spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); error = nfs_safe_remove(dentry); if (!error) { nfs_set_verifier(dentry, nfs_save_change_attribute(dir)); } else if (need_rehash) d_rehash(dentry); unlock_kernel(); return error;}/* * To create a symbolic link, most file systems instantiate a new inode, * add a page to it containing the path, then write it out to the disk * using prepare_write/commit_write. * * Unfortunately the NFS client can't create the in-core inode first * because it needs a file handle to create an in-core inode (see * fs/nfs/inode.c:nfs_fhget). We only have a file handle *after* the * symlink request has completed on the server. * * So instead we allocate a raw page, copy the symname into it, then do * the SYMLINK request with the page as the buffer. If it succeeds, we * now have a new file handle and can instantiate an in-core NFS inode * and move the raw page into its mapping. */static int nfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname){ struct pagevec lru_pvec; struct page *page; char *kaddr; struct iattr attr; unsigned int pathlen = strlen(symname); int error; dfprintk(VFS, "NFS: symlink(%s/%ld, %s, %s)\n", dir->i_sb->s_id, dir->i_ino, dentry->d_name.name, symname); if (pathlen > PAGE_SIZE) return -ENAMETOOLONG; attr.ia_mode = S_IFLNK | S_IRWXUGO; attr.ia_valid = ATTR_MODE; lock_kernel(); page = alloc_page(GFP_HIGHUSER); if (!page) { unlock_kernel();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -