static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, struct inode *newdir, struct dentry *newent, unsigned int flags, int opcode, size_t argsize) { int err; struct fuse_rename2_in inarg; struct fuse_conn *fc = get_fuse_conn(olddir); FUSE_ARGS(args); memset(&inarg, 0, argsize); inarg.newdir = get_node_id(newdir); inarg.flags = flags; args.in.h.opcode = opcode; args.in.h.nodeid = get_node_id(olddir); args.in.numargs = 3; args.in.args[0].size = argsize; args.in.args[0].value = &inarg; args.in.args[1].size = oldent->d_name.len + 1; args.in.args[1].value = oldent->d_name.name; args.in.args[2].size = newent->d_name.len + 1; args.in.args[2].value = newent->d_name.name; err = fuse_simple_request(fc, &args); if (!err) { /* ctime changes */ fuse_invalidate_attr(d_inode(oldent)); fuse_update_ctime(d_inode(oldent)); if (flags & RENAME_EXCHANGE) { fuse_invalidate_attr(d_inode(newent)); fuse_update_ctime(d_inode(newent)); } fuse_invalidate_attr(olddir); if (olddir != newdir) fuse_invalidate_attr(newdir); /* newent will end up negative */ if (!(flags & RENAME_EXCHANGE) && d_really_is_positive(newent)) { fuse_invalidate_attr(d_inode(newent)); fuse_invalidate_entry_cache(newent); fuse_update_ctime(d_inode(newent)); } } else if (err == -EINTR) { /* If request was interrupted, DEITY only knows if the rename actually took place. If the invalidation fails (e.g. some process has CWD under the renamed directory), then there can be inconsistency between the dcache and the real filesystem. Tough luck. */ fuse_invalidate_entry(oldent); if (d_really_is_positive(newent)) fuse_invalidate_entry(newent); } return err; }
static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { int res; if (flags & ~RENAME_NOREPLACE) return -EINVAL; /* Unlink destination if it already exists */ if (d_really_is_positive(new_dentry)) { if (d_is_dir(new_dentry)) res = hfsplus_rmdir(new_dir, new_dentry); else res = hfsplus_unlink(new_dir, new_dentry); if (res) return res; } res = hfsplus_rename_cat((u32)(unsigned long)old_dentry->d_fsdata, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) new_dentry->d_fsdata = old_dentry->d_fsdata; return res; }
/* * simple tests for the del-entry operations. * following the checks in vfs, plus the parent-child relationship. */ int au_may_del(struct dentry *dentry, aufs_bindex_t bindex, struct dentry *h_parent, int isdir) { int err; umode_t h_mode; struct dentry *h_dentry, *h_latest; struct inode *h_inode; h_dentry = au_h_dptr(dentry, bindex); if (d_really_is_positive(dentry)) { err = -ENOENT; if (unlikely(d_is_negative(h_dentry))) goto out; h_inode = d_inode(h_dentry); if (unlikely(!h_inode->i_nlink)) goto out; h_mode = h_inode->i_mode; if (!isdir) { err = -EISDIR; if (unlikely(S_ISDIR(h_mode))) goto out; } else if (unlikely(!S_ISDIR(h_mode))) { err = -ENOTDIR; goto out; } } else { /* rename(2) case */ err = -EIO; if (unlikely(d_is_positive(h_dentry))) goto out; } err = -ENOENT; /* expected parent dir is locked */ if (unlikely(h_parent != h_dentry->d_parent)) goto out; err = 0; /* * rmdir a dir may break the consistency on some filesystem. * let's try heavy test. */ err = -EACCES; if (unlikely(!au_opt_test(au_mntflags(dentry->d_sb), DIRPERM1) && au_test_h_perm(d_inode(h_parent), MAY_EXEC | MAY_WRITE))) goto out; h_latest = au_sio_lkup_one(&dentry->d_name, h_parent); err = -EIO; if (IS_ERR(h_latest)) goto out; if (h_latest == h_dentry) err = 0; dput(h_latest); out: return err; }
/* * hfs_rename() * * This is the rename() entry in the inode_operations structure for * regular HFS directories. The purpose is to rename an existing * file or directory, given the inode for the current directory and * the name (and its length) of the existing file/directory and the * inode for the new directory and the name (and its length) of the * new file/directory. * XXX: how do you handle must_be dir? */ static int hfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry, unsigned int flags) { int res; if (flags & ~RENAME_NOREPLACE) return -EINVAL; /* Unlink destination if it already exists */ if (d_really_is_positive(new_dentry)) { res = hfs_remove(new_dir, new_dentry); if (res) return res; } res = hfs_cat_move(d_inode(old_dentry)->i_ino, old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); if (!res) hfs_cat_build_key(old_dir->i_sb, (btree_key *)&HFS_I(d_inode(old_dentry))->cat_key, new_dir->i_ino, &new_dentry->d_name); return res; }
static int remove_file(struct dentry *parent, char *name) { struct dentry *tmp; int ret; tmp = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(tmp)) { ret = PTR_ERR(tmp); goto bail; } spin_lock(&tmp->d_lock); if (!d_unhashed(tmp) && d_really_is_positive(tmp)) { __d_drop(tmp); spin_unlock(&tmp->d_lock); simple_unlink(d_inode(parent), tmp); } else { spin_unlock(&tmp->d_lock); } dput(tmp); ret = 0; bail: /* * We don't expect clients to care about the return value, but * it's there if they need it. */ return ret; }
static int gfs2_atomic_open(struct inode *dir, struct dentry *dentry, struct file *file, unsigned flags, umode_t mode) { struct dentry *d; bool excl = !!(flags & O_EXCL); if (!d_in_lookup(dentry)) goto skip_lookup; d = __gfs2_lookup(dir, dentry, file); if (IS_ERR(d)) return PTR_ERR(d); if (d != NULL) dentry = d; if (d_really_is_positive(dentry)) { if (!(file->f_mode & FMODE_OPENED)) return finish_no_open(file, d); dput(d); return 0; } BUG_ON(d != NULL); skip_lookup: if (!(flags & O_CREAT)) return -ENOENT; return gfs2_create_inode(dir, dentry, file, S_IFREG | mode, 0, NULL, 0, excl); }
/** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up * @flags: lookup flags * * Find a file on disk. If the file does not exist, then we'll add it to the * dentry cache and continue on to read it from the disk. */ static struct dentry *ecryptfs_lookup(struct inode *ecryptfs_dir_inode, struct dentry *ecryptfs_dentry, unsigned int flags) { char *encrypted_and_encoded_name = NULL; size_t encrypted_and_encoded_name_size; struct ecryptfs_mount_crypt_stat *mount_crypt_stat = NULL; struct dentry *lower_dir_dentry, *lower_dentry; int rc = 0; lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name, lower_dir_dentry, ecryptfs_dentry->d_name.len); mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " "[%d] on lower_dentry = [%pd]\n", __func__, rc, ecryptfs_dentry); goto out; } if (d_really_is_positive(lower_dentry)) goto interpose; mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; if (!(mount_crypt_stat && (mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES))) goto interpose; dput(lower_dentry); rc = ecryptfs_encrypt_and_encode_filename( &encrypted_and_encoded_name, &encrypted_and_encoded_name_size, NULL, mount_crypt_stat, ecryptfs_dentry->d_name.name, ecryptfs_dentry->d_name.len); if (rc) { printk(KERN_ERR "%s: Error attempting to encrypt and encode " "filename; rc = [%d]\n", __func__, rc); goto out; } mutex_lock(&d_inode(lower_dir_dentry)->i_mutex); lower_dentry = lookup_one_len(encrypted_and_encoded_name, lower_dir_dentry, encrypted_and_encoded_name_size); mutex_unlock(&d_inode(lower_dir_dentry)->i_mutex); if (IS_ERR(lower_dentry)) { rc = PTR_ERR(lower_dentry); ecryptfs_printk(KERN_DEBUG, "%s: lookup_one_len() returned " "[%d] on lower_dentry = [%s]\n", __func__, rc, encrypted_and_encoded_name); goto out; } interpose: rc = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry, ecryptfs_dir_inode); out: kfree(encrypted_and_encoded_name); return ERR_PTR(rc); }
static int logfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { if (d_really_is_positive(new_dentry)) return logfs_rename_target(old_dir, old_dentry, new_dir, new_dentry); return logfs_rename_cross(old_dir, old_dentry, new_dir, new_dentry); }
int affs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct super_block *sb = old_dir->i_sb; struct buffer_head *bh = NULL; int retval; pr_debug("%s(old=%lu,\"%pd\" to new=%lu,\"%pd\")\n", __func__, old_dir->i_ino, old_dentry, new_dir->i_ino, new_dentry); retval = affs_check_name(new_dentry->d_name.name, new_dentry->d_name.len, affs_nofilenametruncate(old_dentry)); if (retval) return retval; /* Unlink destination if it already exists */ if (d_really_is_positive(new_dentry)) { retval = affs_remove_header(new_dentry); if (retval) return retval; } bh = affs_bread(sb, d_inode(old_dentry)->i_ino); if (!bh) return -EIO; /* Remove header from its parent directory. */ affs_lock_dir(old_dir); retval = affs_remove_hash(old_dir, bh); affs_unlock_dir(old_dir); if (retval) goto done; /* And insert it into the new directory with the new name. */ affs_copy_name(AFFS_TAIL(sb, bh)->name, new_dentry); affs_fix_checksum(sb, bh); affs_lock_dir(new_dir); retval = affs_insert_hash(new_dir, bh); affs_unlock_dir(new_dir); /* TODO: move it back to old_dir, if error? */ done: mark_buffer_dirty_inode(bh, retval ? old_dir : new_dir); affs_brelse(bh); return retval; }
static int cifs_d_revalidate(struct dentry *direntry, unsigned int flags) { if (flags & LOOKUP_RCU) return -ECHILD; if (d_really_is_positive(direntry)) { if (cifs_revalidate_dentry(direntry)) return 0; else { /* * If the inode wasn't known to be a dfs entry when * the dentry was instantiated, such as when created * via ->readdir(), it needs to be set now since the * attributes will have been updated by * cifs_revalidate_dentry(). */ if (IS_AUTOMOUNT(d_inode(direntry)) && !(direntry->d_flags & DCACHE_NEED_AUTOMOUNT)) { spin_lock(&direntry->d_lock); direntry->d_flags |= DCACHE_NEED_AUTOMOUNT; spin_unlock(&direntry->d_lock); } return 1; } } /* * This may be nfsd (or something), anyway, we can't see the * intent of this. So, since this can be for creation, drop it. */ if (!flags) return 0; /* * Drop the negative dentry, in order to make sure to use the * case sensitive name which is specified by user if this is * for creation. */ if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET)) return 0; if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled) return 0; return 1; }
/* * Encode post-operation attributes. * The inode may be NULL if the call failed because of a stale file * handle. In this case, no attributes are returned. */ static __be32 * encode_post_op_attr(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp) { struct dentry *dentry = fhp->fh_dentry; if (dentry && d_really_is_positive(dentry)) { __be32 err; struct kstat stat; err = fh_getattr(fhp, &stat); if (!err) { *p++ = xdr_one; /* attributes follow */ lease_get_mtime(d_inode(dentry), &stat.mtime); return encode_fattr3(rqstp, p, fhp, &stat); } } *p++ = xdr_zero; return p; }
static void au_do_hide(struct dentry *dentry) { struct inode *inode; if (d_really_is_positive(dentry)) { inode = d_inode(dentry); if (!d_is_dir(dentry)) { if (inode->i_nlink && !d_unhashed(dentry)) drop_nlink(inode); } else { clear_nlink(inode); /* stop next lookup */ inode->i_flags |= S_DEAD; } smp_mb(); /* necessary? */ } d_drop(dentry); }
/* GETACL */ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_getaclres *resp) { struct dentry *dentry = resp->fh.fh_dentry; p = nfs3svc_encode_post_op_attr(rqstp, p, &resp->fh); if (resp->status == 0 && dentry && d_really_is_positive(dentry)) { struct inode *inode = d_inode(dentry); struct kvec *head = rqstp->rq_res.head; unsigned int base; int n; int w; *p++ = htonl(resp->mask); if (!xdr_ressize_check(rqstp, p)) return 0; base = (char *)p - (char *)head->iov_base; rqstp->rq_res.page_len = w = nfsacl_size( (resp->mask & NFS_ACL) ? resp->acl_access : NULL, (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); while (w > 0) { if (!*(rqstp->rq_next_page++)) return 0; w -= PAGE_SIZE; } n = nfsacl_encode(&rqstp->rq_res, base, inode, resp->acl_access, resp->mask & NFS_ACL, 0); if (n > 0) n = nfsacl_encode(&rqstp->rq_res, base + n, inode, resp->acl_default, resp->mask & NFS_DFACL, NFS_ACL_DEFAULT); if (n <= 0) return 0; } else if (!xdr_ressize_check(rqstp, p)) return 0; return 1; }
static int fuse_atomic_open(struct inode *dir, struct dentry *entry, struct file *file, unsigned flags, umode_t mode, int *opened) { int err; struct fuse_conn *fc = get_fuse_conn(dir); struct dentry *res = NULL; if (d_in_lookup(entry)) { res = fuse_lookup(dir, entry, 0); if (IS_ERR(res)) return PTR_ERR(res); if (res) entry = res; } if (!(flags & O_CREAT) || d_really_is_positive(entry)) goto no_open; /* Only creates */ *opened |= FILE_CREATED; if (fc->no_create) goto mknod; err = fuse_create_open(dir, entry, file, flags, mode, opened); if (err == -ENOSYS) { fc->no_create = 1; goto mknod; } out_dput: dput(res); return err; mknod: err = fuse_mknod(dir, entry, mode, 0); if (err) goto out_dput; no_open: return finish_no_open(file, res); }
/* * Enocde weak cache consistency data */ static __be32 * encode_wcc_data(struct svc_rqst *rqstp, __be32 *p, struct svc_fh *fhp) { struct dentry *dentry = fhp->fh_dentry; if (dentry && d_really_is_positive(dentry) && fhp->fh_post_saved) { if (fhp->fh_pre_saved) { *p++ = xdr_one; p = xdr_encode_hyper(p, (u64) fhp->fh_pre_size); p = encode_time3(p, &fhp->fh_pre_mtime); p = encode_time3(p, &fhp->fh_pre_ctime); } else { *p++ = xdr_zero; } return encode_saved_post_attr(rqstp, p, fhp); } /* no pre- or post-attrs */ *p++ = xdr_zero; return encode_post_op_attr(rqstp, p, fhp); }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; int rc; lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); dput(lower_dentry); if (!rc && d_really_is_positive(dentry)) clear_nlink(d_inode(dentry)); fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); set_nlink(dir, d_inode(lower_dir_dentry)->i_nlink); unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
static int dev_rmdir(const char *name) { struct path parent; struct dentry *dentry; int err; dentry = kern_path_locked(name, &parent); if (IS_ERR(dentry)) return PTR_ERR(dentry); if (d_really_is_positive(dentry)) { if (d_inode(dentry)->i_private == &thread) err = vfs_rmdir(d_inode(parent.dentry), dentry); else err = -EPERM; } else { err = -ENOENT; } dput(dentry); mutex_unlock(&d_inode(parent.dentry)->i_mutex); path_put(&parent); return err; }
/* * remove a directory from an AFS filesystem */ static int afs_rmdir(struct inode *dir, struct dentry *dentry) { struct afs_vnode *dvnode, *vnode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%pd}", dvnode->fid.vid, dvnode->fid.vnode, dentry); key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, true); if (ret < 0) goto rmdir_error; if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); clear_nlink(&vnode->vfs_inode); set_bit(AFS_VNODE_DELETED, &vnode->flags); afs_discard_callback_on_delete(vnode); } key_put(key); _leave(" = 0"); return 0; rmdir_error: key_put(key); error: _leave(" = %d", ret); return ret; }
/* * do a lookup in a directory * - just returns the FID the dentry name maps to if found */ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, struct afs_fid *fid, struct key *key) { struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_lookup_cookie cookie = { .ctx.actor = afs_lookup_filldir, .name = dentry->d_name, .fid.vid = as->volume->vid }; int ret; _enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry); /* search the directory */ ret = afs_dir_iterate(dir, &cookie.ctx, key); if (ret < 0) { _leave(" = %d [iter]", ret); return ret; } ret = -ENOENT; if (!cookie.found) { _leave(" = -ENOENT [not found]"); return -ENOENT; } *fid = cookie.fid; _leave(" = 0 { vn=%u u=%u }", fid->vnode, fid->unique); return 0; } /* * Try to auto mount the mountpoint with pseudo directory, if the autocell * operation is setted. */ static struct inode *afs_try_auto_mntpt( int ret, struct dentry *dentry, struct inode *dir, struct key *key, struct afs_fid *fid) { const char *devname = dentry->d_name.name; struct afs_vnode *vnode = AFS_FS_I(dir); struct inode *inode; _enter("%d, %p{%pd}, {%x:%u}, %p", ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key); if (ret != -ENOENT || !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) goto out; inode = afs_iget_autocell(dir, devname, strlen(devname), key); if (IS_ERR(inode)) { ret = PTR_ERR(inode); goto out; } *fid = AFS_FS_I(inode)->fid; _leave("= %p", inode); return inode; out: _leave("= %d", ret); return ERR_PTR(ret); } /* * look up an entry in a directory */ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct afs_vnode *vnode; struct afs_fid fid; struct inode *inode; struct key *key; int ret; vnode = AFS_FS_I(dir); _enter("{%x:%u},%p{%pd},", vnode->fid.vid, vnode->fid.vnode, dentry, dentry); ASSERTCMP(d_inode(dentry), ==, NULL); if (dentry->d_name.len >= AFSNAMEMAX) { _leave(" = -ENAMETOOLONG"); return ERR_PTR(-ENAMETOOLONG); } if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { _leave(" = -ESTALE"); return ERR_PTR(-ESTALE); } key = afs_request_key(vnode->volume->cell); if (IS_ERR(key)) { _leave(" = %ld [key]", PTR_ERR(key)); return ERR_CAST(key); } ret = afs_validate(vnode, key); if (ret < 0) { key_put(key); _leave(" = %d [val]", ret); return ERR_PTR(ret); } ret = afs_do_lookup(dir, dentry, &fid, key); if (ret < 0) { inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); if (!IS_ERR(inode)) { key_put(key); goto success; } ret = PTR_ERR(inode); key_put(key); if (ret == -ENOENT) { d_add(dentry, NULL); _leave(" = NULL [negative]"); return NULL; } _leave(" = %d [do]", ret); return ERR_PTR(ret); } dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; /* instantiate the dentry */ inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL); key_put(key); if (IS_ERR(inode)) { _leave(" = %ld", PTR_ERR(inode)); return ERR_CAST(inode); } success: d_add(dentry, inode); _leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }", fid.vnode, fid.unique, d_inode(dentry)->i_ino, d_inode(dentry)->i_generation); return NULL; } /* * check that a dentry lookup hit has found a valid entry * - NOTE! the hit can be a negative hit too, so we can't assume we have an * inode */ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) { struct afs_vnode *vnode, *dir; struct afs_fid uninitialized_var(fid); struct dentry *parent; struct key *key; void *dir_version; int ret; if (flags & LOOKUP_RCU) return -ECHILD; vnode = AFS_FS_I(d_inode(dentry)); if (d_really_is_positive(dentry)) _enter("{v={%x:%u} n=%pd fl=%lx},", vnode->fid.vid, vnode->fid.vnode, dentry, vnode->flags); else _enter("{neg n=%pd}", dentry); key = afs_request_key(AFS_FS_S(dentry->d_sb)->volume->cell); if (IS_ERR(key)) key = NULL; /* lock down the parent dentry so we can peer at it */ parent = dget_parent(dentry); dir = AFS_FS_I(d_inode(parent)); /* validate the parent directory */ if (test_bit(AFS_VNODE_MODIFIED, &dir->flags)) afs_validate(dir, key); if (test_bit(AFS_VNODE_DELETED, &dir->flags)) { _debug("%pd: parent dir deleted", dentry); goto out_bad; } dir_version = (void *) (unsigned long) dir->status.data_version; if (dentry->d_fsdata == dir_version) goto out_valid; /* the dir contents are unchanged */ _debug("dir modified"); /* search the directory for this vnode */ ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); switch (ret) { case 0: /* the filename maps to something */ if (d_really_is_negative(dentry)) goto out_bad; if (is_bad_inode(d_inode(dentry))) { printk("kAFS: afs_d_revalidate: %pd2 has bad inode\n", dentry); goto out_bad; } /* if the vnode ID has changed, then the dirent points to a * different file */ if (fid.vnode != vnode->fid.vnode) { _debug("%pd: dirent changed [%u != %u]", dentry, fid.vnode, vnode->fid.vnode); goto not_found; } /* if the vnode ID uniqifier has changed, then the file has * been deleted and replaced, and the original vnode ID has * been reused */ if (fid.unique != vnode->fid.unique) { _debug("%pd: file deleted (uq %u -> %u I:%u)", dentry, fid.unique, vnode->fid.unique, d_inode(dentry)->i_generation); spin_lock(&vnode->lock); set_bit(AFS_VNODE_DELETED, &vnode->flags); spin_unlock(&vnode->lock); goto not_found; } goto out_valid; case -ENOENT: /* the filename is unknown */ _debug("%pd: dirent not found", dentry); if (d_really_is_positive(dentry)) goto not_found; goto out_valid; default: _debug("failed to iterate dir %pd: %d", parent, ret); goto out_bad; } out_valid: dentry->d_fsdata = dir_version; dput(parent); key_put(key); _leave(" = 1 [valid]"); return 1; /* the dirent, if it exists, now points to a different vnode */ not_found: spin_lock(&dentry->d_lock); dentry->d_flags |= DCACHE_NFSFS_RENAMED; spin_unlock(&dentry->d_lock); out_bad: _debug("dropping dentry %pd2", dentry); dput(parent); key_put(key); _leave(" = 0 [bad]"); return 0; } /* * allow the VFS to enquire as to whether a dentry should be unhashed (mustn't * sleep) * - called from dput() when d_count is going to 0. * - return 1 to request dentry be unhashed, 0 otherwise */ static int afs_d_delete(const struct dentry *dentry) { _enter("%pd", dentry); if (dentry->d_flags & DCACHE_NFSFS_RENAMED) goto zap; if (d_really_is_positive(dentry) && (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(d_inode(dentry))->flags) || test_bit(AFS_VNODE_PSEUDODIR, &AFS_FS_I(d_inode(dentry))->flags))) goto zap; _leave(" = 0 [keep]"); return 0; zap: _leave(" = 1 [zap]"); return 1; }
int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid, u64 child_nodeid, struct qstr *name) { int err = -ENOTDIR; struct inode *parent; struct dentry *dir; struct dentry *entry; parent = ilookup5(sb, parent_nodeid, fuse_inode_eq, &parent_nodeid); if (!parent) return -ENOENT; inode_lock(parent); if (!S_ISDIR(parent->i_mode)) goto unlock; err = -ENOENT; dir = d_find_alias(parent); if (!dir) goto unlock; name->hash = full_name_hash(dir, name->name, name->len); entry = d_lookup(dir, name); dput(dir); if (!entry) goto unlock; fuse_invalidate_attr(parent); fuse_invalidate_entry(entry); if (child_nodeid != 0 && d_really_is_positive(entry)) { inode_lock(d_inode(entry)); if (get_node_id(d_inode(entry)) != child_nodeid) { err = -ENOENT; goto badentry; } if (d_mountpoint(entry)) { err = -EBUSY; goto badentry; } if (d_is_dir(entry)) { shrink_dcache_parent(entry); if (!simple_empty(entry)) { err = -ENOTEMPTY; goto badentry; } d_inode(entry)->i_flags |= S_DEAD; } dont_mount(entry); clear_nlink(d_inode(entry)); err = 0; badentry: inode_unlock(d_inode(entry)); if (!err) d_delete(entry); } else { err = 0; } dput(entry); unlock: inode_unlock(parent); iput(parent); return err; }
/** * securityfs_create_file - create a file in the securityfs filesystem * * @name: a pointer to a string containing the name of the file to create. * @mode: the permission that the file should have * @parent: a pointer to the parent dentry for this file. This should be a * directory dentry if set. If this parameter is %NULL, then the * file will be created in the root of the securityfs filesystem. * @data: a pointer to something that the caller will want to get to later * on. The inode.i_private pointer will point to this value on * the open() call. * @fops: a pointer to a struct file_operations that should be used for * this file. * * This is the basic "create a file" function for securityfs. It allows for a * wide range of flexibility in creating a file, or a directory (if you * want to create a directory, the securityfs_create_dir() function is * recommended to be used instead). * * This function returns a pointer to a dentry if it succeeds. This * pointer must be passed to the securityfs_remove() function when the file is * to be removed (no automatic cleanup happens if your module is unloaded, * you are responsible here). If an error occurs, the function will return * the error value (via ERR_PTR). * * If securityfs is not enabled in the kernel, the value %-ENODEV is * returned. */ struct dentry *securityfs_create_file(const char *name, umode_t mode, struct dentry *parent, void *data, const struct file_operations *fops) { struct dentry *dentry; int is_dir = S_ISDIR(mode); struct inode *dir, *inode; int error; if (!is_dir) { BUG_ON(!fops); mode = (mode & S_IALLUGO) | S_IFREG; } pr_debug("securityfs: creating file '%s'\n",name); error = simple_pin_fs(&fs_type, &mount, &mount_count); if (error) return ERR_PTR(error); if (!parent) parent = mount->mnt_root; dir = d_inode(parent); inode_lock(dir); dentry = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(dentry)) goto out; if (d_really_is_positive(dentry)) { error = -EEXIST; goto out1; } inode = new_inode(dir->i_sb); if (!inode) { error = -ENOMEM; goto out1; } inode->i_ino = get_next_ino(); inode->i_mode = mode; inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; inode->i_private = data; if (is_dir) { inode->i_op = &simple_dir_inode_operations; inode->i_fop = &simple_dir_operations; inc_nlink(inode); inc_nlink(dir); } else { inode->i_fop = fops; } d_instantiate(dentry, inode); dget(dentry); inode_unlock(dir); return dentry; out1: dput(dentry); dentry = ERR_PTR(error); out: inode_unlock(dir); simple_release_fs(&mount, &mount_count); return dentry; }
/* Check if 'dentry' should expire, or return a nearby * dentry that is suitable. * If returned dentry is different from arg dentry, * then a dget() reference was taken, else not. */ static struct dentry *should_expire(struct dentry *dentry, struct vfsmount *mnt, unsigned long timeout, int how) { int do_now = how & AUTOFS_EXP_IMMEDIATE; int exp_leaves = how & AUTOFS_EXP_LEAVES; struct autofs_info *ino = autofs4_dentry_ino(dentry); unsigned int ino_count; /* No point expiring a pending mount */ if (ino->flags & AUTOFS_INF_PENDING) return NULL; /* * Case 1: (i) indirect mount or top level pseudo direct mount * (autofs-4.1). * (ii) indirect mount with offset mount, check the "/" * offset (autofs-5.0+). */ if (d_mountpoint(dentry)) { pr_debug("checking mountpoint %p %pd\n", dentry, dentry); /* Can we umount this guy */ if (autofs4_mount_busy(mnt, dentry)) return NULL; /* Can we expire this guy */ if (autofs4_can_expire(dentry, timeout, do_now)) return dentry; return NULL; } if (d_really_is_positive(dentry) && d_is_symlink(dentry)) { pr_debug("checking symlink %p %pd\n", dentry, dentry); /* * A symlink can't be "busy" in the usual sense so * just check last used for expire timeout. */ if (autofs4_can_expire(dentry, timeout, do_now)) return dentry; return NULL; } if (simple_empty(dentry)) return NULL; /* Case 2: tree mount, expire iff entire tree is not busy */ if (!exp_leaves) { /* Path walk currently on this dentry? */ ino_count = atomic_read(&ino->count) + 1; if (d_count(dentry) > ino_count) return NULL; if (!autofs4_tree_busy(mnt, dentry, timeout, do_now)) return dentry; /* * Case 3: pseudo direct mount, expire individual leaves * (autofs-4.1). */ } else { /* Path walk currently on this dentry? */ struct dentry *expired; ino_count = atomic_read(&ino->count) + 1; if (d_count(dentry) > ino_count) return NULL; expired = autofs4_check_leaves(mnt, dentry, timeout, do_now); if (expired) { if (expired == dentry) dput(dentry); return expired; } } return NULL; }
static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry, struct inode *new_dir_i, struct dentry *new_dentry) { int ret; struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); struct jffs2_inode_info *victim_f = NULL; uint8_t type; uint32_t now; /* The VFS will check for us and prevent trying to rename a * file over a directory and vice versa, but if it's a directory, * the VFS can't check whether the victim is empty. The filesystem * needs to do that for itself. */ if (d_really_is_positive(new_dentry)) { victim_f = JFFS2_INODE_INFO(d_inode(new_dentry)); if (d_is_dir(new_dentry)) { struct jffs2_full_dirent *fd; mutex_lock(&victim_f->sem); for (fd = victim_f->dents; fd; fd = fd->next) { if (fd->ino) { mutex_unlock(&victim_f->sem); return -ENOTEMPTY; } } mutex_unlock(&victim_f->sem); } } /* XXX: We probably ought to alloc enough space for both nodes at the same time. Writing the new link, then getting -ENOSPC, is quite bad :) */ /* Make a hard link */ /* XXX: This is ugly */ type = (d_inode(old_dentry)->i_mode & S_IFMT) >> 12; if (!type) type = DT_REG; now = get_seconds(); ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), d_inode(old_dentry)->i_ino, type, new_dentry->d_name.name, new_dentry->d_name.len, now); if (ret) return ret; if (victim_f) { /* There was a victim. Kill it off nicely */ if (d_is_dir(new_dentry)) clear_nlink(d_inode(new_dentry)); else drop_nlink(d_inode(new_dentry)); /* Don't oops if the victim was a dirent pointing to an inode which didn't exist. */ if (victim_f->inocache) { mutex_lock(&victim_f->sem); if (d_is_dir(new_dentry)) victim_f->inocache->pino_nlink = 0; else victim_f->inocache->pino_nlink--; mutex_unlock(&victim_f->sem); } } /* If it was a directory we moved, and there was no victim, increase i_nlink on its new parent */ if (d_is_dir(old_dentry) && !victim_f) inc_nlink(new_dir_i); /* Unlink the original */ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), old_dentry->d_name.name, old_dentry->d_name.len, NULL, now); /* We don't touch inode->i_nlink */ if (ret) { /* Oh shit. We really ought to make a single node which can do both atomically */ struct jffs2_inode_info *f = JFFS2_INODE_INFO(d_inode(old_dentry)); mutex_lock(&f->sem); inc_nlink(d_inode(old_dentry)); if (f->inocache && !d_is_dir(old_dentry)) f->inocache->pino_nlink++; mutex_unlock(&f->sem); pr_notice("%s(): Link succeeded, unlink failed (err %d). You now have a hard link\n", __func__, ret); /* * We can't keep the target in dcache after that. * For one thing, we can't afford dentry aliases for directories. * For another, if there was a victim, we _can't_ set new inode * for that sucker and we have to trigger mount eviction - the * caller won't do it on its own since we are returning an error. */ d_invalidate(new_dentry); new_dir_i->i_mtime = new_dir_i->i_ctime = ITIME(now); return ret; } if (d_is_dir(old_dentry)) drop_nlink(old_dir_i); new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now); return 0; }
/* * if valid returns 1, otherwise 0. */ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags) { int valid, err; unsigned int sigen; unsigned char do_udba; struct super_block *sb; struct inode *inode; /* todo: support rcu-walk? */ if (flags & LOOKUP_RCU) return -ECHILD; valid = 0; if (unlikely(!au_di(dentry))) goto out; valid = 1; sb = dentry->d_sb; /* * todo: very ugly * i_mutex of parent dir may be held, * but we should not return 'invalid' due to busy. */ err = aufs_read_lock(dentry, AuLock_FLUSH | AuLock_DW | AuLock_NOPLM); if (unlikely(err)) { valid = err; AuTraceErr(err); goto out; } inode = NULL; if (d_really_is_positive(dentry)) inode = d_inode(dentry); if (unlikely(inode && is_bad_inode(inode))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } if (unlikely(au_dbrange_test(dentry))) { err = -EINVAL; AuTraceErr(err); goto out_dgrade; } sigen = au_sigen(sb); if (au_digen_test(dentry, sigen)) { AuDebugOn(IS_ROOT(dentry)); err = au_reval_dpath(dentry, sigen); if (unlikely(err)) { AuTraceErr(err); goto out_dgrade; } } di_downgrade_lock(dentry, AuLock_IR); err = -EINVAL; if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY)) && inode && !(inode->i_state && I_LINKABLE) && (IS_DEADDIR(inode) || !inode->i_nlink)) goto out_inval; do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE); if (do_udba && inode) { aufs_bindex_t bstart = au_ibstart(inode); struct inode *h_inode; if (bstart >= 0) { h_inode = au_h_iptr(inode, bstart); if (h_inode && au_test_higen(inode, h_inode)) goto out_inval; } } err = h_d_revalidate(dentry, inode, flags, do_udba); if (unlikely(!err && do_udba && au_dbstart(dentry) < 0)) { err = -EIO; AuDbg("both of real entry and whiteout found, %p, err %d\n", dentry, err); } goto out_inval; out_dgrade: di_downgrade_lock(dentry, AuLock_IR); out_inval: aufs_read_unlock(dentry, AuLock_IR); AuTraceErr(err); valid = !err; out: if (!valid) { AuDbg("%pd invalid, %d\n", dentry, valid); d_drop(dentry); } return valid; }
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) { int err, ebrange; unsigned int sigen; struct au_dinfo *dinfo, *tmp; struct super_block *sb; struct inode *inode; DiMustWriteLock(dentry); AuDebugOn(IS_ROOT(dentry)); AuDebugOn(d_really_is_negative(parent)); sb = dentry->d_sb; sigen = au_sigen(sb); err = au_digen_test(parent, sigen); if (unlikely(err)) goto out; dinfo = au_di(dentry); err = au_di_realloc(dinfo, au_sbend(sb) + 1); if (unlikely(err)) goto out; ebrange = au_dbrange_test(dentry); if (!ebrange) ebrange = au_do_refresh_hdentry(dentry, parent); if (d_unhashed(dentry) || ebrange /* || dinfo->di_tmpfile */) { AuDebugOn(au_dbstart(dentry) < 0 && au_dbend(dentry) >= 0); if (d_really_is_positive(dentry)) { inode = d_inode(dentry); err = au_refresh_hinode_self(inode); } au_dbg_verify_dinode(dentry); if (!err) goto out_dgen; /* success */ goto out; } /* temporary dinfo */ AuDbgDentry(dentry); err = -ENOMEM; tmp = au_di_alloc(sb, AuLsc_DI_TMP); if (unlikely(!tmp)) goto out; au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ /* * if current working dir is removed, it returns an error. * but the dentry is legal. */ err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); AuDbgDentry(dentry); au_di_swap(tmp, dinfo); if (err == -ENOENT) err = 0; if (err >= 0) { /* compare/refresh by dinfo */ AuDbgDentry(dentry); err = au_refresh_by_dinfo(dentry, dinfo, tmp); au_dbg_verify_dinode(dentry); AuTraceErr(err); } au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); if (unlikely(err)) goto out; out_dgen: au_update_digen(dentry); out: if (unlikely(err && !(dentry->d_flags & DCACHE_NFSFS_RENAMED))) { AuIOErr("failed refreshing %pd, %d\n", dentry, err); AuDbgDentry(dentry); } AuTraceErr(err); return err; }
/* * By adding a dirty branch, a cached dentry may be affected in various ways. * * a dirty branch is added * - on the top of layers * - in the middle of layers * - to the bottom of layers * * on the added branch there exists * - a whiteout * - a diropq * - a same named entry * + exist * * negative --> positive * * positive --> positive * - type is unchanged * - type is changed * + doesn't exist * * negative --> negative * * positive --> negative (rejected by au_br_del() for non-dir case) * - none */ static int au_refresh_by_dinfo(struct dentry *dentry, struct au_dinfo *dinfo, struct au_dinfo *tmp) { int err; aufs_bindex_t bindex, bend; struct { struct dentry *dentry; struct inode *inode; mode_t mode; } orig_h, tmp_h; struct au_hdentry *hd; struct inode *inode, *h_inode; struct dentry *h_dentry; err = 0; AuDebugOn(dinfo->di_bstart < 0); orig_h.mode = 0; orig_h.dentry = dinfo->di_hdentry[dinfo->di_bstart].hd_dentry; orig_h.inode = NULL; if (d_is_positive(orig_h.dentry)) { orig_h.inode = d_inode(orig_h.dentry); orig_h.mode = orig_h.inode->i_mode & S_IFMT; } memset(&tmp_h, 0, sizeof(tmp_h)); if (tmp->di_bstart >= 0) { tmp_h.dentry = tmp->di_hdentry[tmp->di_bstart].hd_dentry; tmp_h.inode = NULL; if (d_is_positive(tmp_h.dentry)) { tmp_h.inode = d_inode(tmp_h.dentry); tmp_h.mode = tmp_h.inode->i_mode & S_IFMT; } } inode = NULL; if (d_really_is_positive(dentry)) inode = d_inode(dentry); if (!orig_h.inode) { AuDbg("nagative originally\n"); if (inode) { au_hide(dentry); goto out; } AuDebugOn(inode); AuDebugOn(dinfo->di_bstart != dinfo->di_bend); AuDebugOn(dinfo->di_bdiropq != -1); if (!tmp_h.inode) { AuDbg("negative --> negative\n"); /* should have only one negative lower */ if (tmp->di_bstart >= 0 && tmp->di_bstart < dinfo->di_bstart) { AuDebugOn(tmp->di_bstart != tmp->di_bend); AuDebugOn(dinfo->di_bstart != dinfo->di_bend); au_set_h_dptr(dentry, dinfo->di_bstart, NULL); au_di_cp(dinfo, tmp); hd = tmp->di_hdentry + tmp->di_bstart; au_set_h_dptr(dentry, tmp->di_bstart, dget(hd->hd_dentry)); } au_dbg_verify_dinode(dentry); } else { AuDbg("negative --> positive\n"); /* * similar to the behaviour of creating with bypassing * aufs. * unhash it in order to force an error in the * succeeding create operation. * we should not set S_DEAD here. */ d_drop(dentry); /* au_di_swap(tmp, dinfo); */ au_dbg_verify_dinode(dentry); } } else { AuDbg("positive originally\n"); /* inode may be NULL */ AuDebugOn(inode && (inode->i_mode & S_IFMT) != orig_h.mode); if (!tmp_h.inode) { AuDbg("positive --> negative\n"); /* or bypassing aufs */ au_hide(dentry); if (tmp->di_bwh >= 0 && tmp->di_bwh <= dinfo->di_bstart) dinfo->di_bwh = tmp->di_bwh; if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } else if (orig_h.mode == tmp_h.mode) { AuDbg("positive --> positive, same type\n"); if (!S_ISDIR(orig_h.mode) && dinfo->di_bstart > tmp->di_bstart) { /* * similar to the behaviour of removing and * creating. */ au_hide(dentry); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } else { /* fill empty slots */ if (dinfo->di_bstart > tmp->di_bstart) dinfo->di_bstart = tmp->di_bstart; if (dinfo->di_bend < tmp->di_bend) dinfo->di_bend = tmp->di_bend; dinfo->di_bwh = tmp->di_bwh; dinfo->di_bdiropq = tmp->di_bdiropq; hd = tmp->di_hdentry; bend = dinfo->di_bend; for (bindex = tmp->di_bstart; bindex <= bend; bindex++) { if (au_h_dptr(dentry, bindex)) continue; h_dentry = hd[bindex].hd_dentry; if (!h_dentry) continue; AuDebugOn(d_is_negative(h_dentry)); h_inode = d_inode(h_dentry); AuDebugOn(orig_h.mode != (h_inode->i_mode & S_IFMT)); au_set_h_dptr(dentry, bindex, dget(h_dentry)); } err = au_refresh_hinode(inode, dentry); au_dbg_verify_dinode(dentry); } } else { AuDbg("positive --> positive, different type\n"); /* similar to the behaviour of removing and creating */ au_hide(dentry); if (inode) err = au_refresh_hinode_self(inode); au_dbg_verify_dinode(dentry); } } out: return err; }
/* * returns positive/negative dentry, NULL or an error. * NULL means whiteout-ed or not-found. */ static struct dentry* au_do_lookup(struct dentry *h_parent, struct dentry *dentry, aufs_bindex_t bindex, struct qstr *wh_name, struct au_do_lookup_args *args) { struct dentry *h_dentry; struct inode *h_inode; struct au_branch *br; int wh_found, opq; unsigned char wh_able; const unsigned char allow_neg = !!au_ftest_lkup(args->flags, ALLOW_NEG); const unsigned char ignore_perm = !!au_ftest_lkup(args->flags, IGNORE_PERM); wh_found = 0; br = au_sbr(dentry->d_sb, bindex); wh_able = !!au_br_whable(br->br_perm); if (wh_able) wh_found = au_wh_test(h_parent, wh_name, /*try_sio*/0); h_dentry = ERR_PTR(wh_found); if (!wh_found) goto real_lookup; if (unlikely(wh_found < 0)) goto out; /* We found a whiteout */ /* au_set_dbend(dentry, bindex); */ au_set_dbwh(dentry, bindex); if (!allow_neg) return NULL; /* success */ real_lookup: if (!ignore_perm) h_dentry = vfsub_lkup_one(&dentry->d_name, h_parent); else h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent); if (IS_ERR(h_dentry)) { if (PTR_ERR(h_dentry) == -ENAMETOOLONG && !allow_neg) h_dentry = NULL; goto out; } h_inode = d_inode(h_dentry); if (d_is_negative(h_dentry)) { if (!allow_neg) goto out_neg; } else if (wh_found || (args->type && args->type != (h_inode->i_mode & S_IFMT))) goto out_neg; if (au_dbend(dentry) <= bindex) au_set_dbend(dentry, bindex); if (au_dbstart(dentry) < 0 || bindex < au_dbstart(dentry)) au_set_dbstart(dentry, bindex); au_set_h_dptr(dentry, bindex, h_dentry); if (!d_is_dir(h_dentry) || !wh_able || (d_really_is_positive(dentry) && !d_is_dir(dentry))) goto out; /* success */ mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); opq = au_diropq_test(h_dentry); mutex_unlock(&h_inode->i_mutex); if (opq > 0) au_set_dbdiropq(dentry, bindex); else if (unlikely(opq < 0)) { au_set_h_dptr(dentry, bindex, NULL); h_dentry = ERR_PTR(opq); } goto out; out_neg: dput(h_dentry); h_dentry = NULL; out: return h_dentry; }
int cifs_hardlink(struct dentry *old_file, struct inode *inode, struct dentry *direntry) { int rc = -EACCES; unsigned int xid; char *from_name = NULL; char *to_name = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink; struct cifs_tcon *tcon; struct TCP_Server_Info *server; struct cifsInodeInfo *cifsInode; tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); tcon = tlink_tcon(tlink); xid = get_xid(); from_name = build_path_from_dentry(old_file); to_name = build_path_from_dentry(direntry); if ((from_name == NULL) || (to_name == NULL)) { rc = -ENOMEM; goto cifs_hl_exit; } if (tcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, tcon, from_name, to_name, cifs_sb->local_nls, cifs_remap(cifs_sb)); else { server = tcon->ses->server; if (!server->ops->create_hardlink) { rc = -ENOSYS; goto cifs_hl_exit; } rc = server->ops->create_hardlink(xid, tcon, from_name, to_name, cifs_sb); if ((rc == -EIO) || (rc == -EINVAL)) rc = -EOPNOTSUPP; } d_drop(direntry); /* force new lookup from server of target */ /* * if source file is cached (oplocked) revalidate will not go to server * until the file is closed or oplock broken so update nlinks locally */ if (d_really_is_positive(old_file)) { cifsInode = CIFS_I(d_inode(old_file)); if (rc == 0) { spin_lock(&d_inode(old_file)->i_lock); inc_nlink(d_inode(old_file)); spin_unlock(&d_inode(old_file)->i_lock); /* * parent dir timestamps will update from srv within a * second, would it really be worth it to set the parent * dir cifs inode time to zero to force revalidate * (faster) for it too? */ } /* * if not oplocked will force revalidate to get info on source * file from srv. Note Samba server prior to 4.2 has bug - * not updating src file ctime on hardlinks but Windows servers * handle it properly */ cifsInode->time = 0; /* * Will update parent dir timestamps from srv within a second. * Would it really be worth it to set the parent dir (cifs * inode) time field to zero to force revalidate on parent * directory faster ie * * CIFS_I(inode)->time = 0; */ } cifs_hl_exit: kfree(from_name); kfree(to_name); free_xid(xid); cifs_put_tlink(tlink); return rc; }
/* * remove a file from an AFS filesystem */ static int afs_unlink(struct inode *dir, struct dentry *dentry) { struct afs_vnode *dvnode, *vnode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%pd}", dvnode->fid.vid, dvnode->fid.vnode, dentry); ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } if (d_really_is_positive(dentry)) { vnode = AFS_FS_I(d_inode(dentry)); /* make sure we have a callback promise on the victim */ ret = afs_validate(vnode, key); if (ret < 0) goto error; } ret = afs_vnode_remove(dvnode, key, dentry->d_name.name, false); if (ret < 0) goto remove_error; if (d_really_is_positive(dentry)) { /* if the file wasn't deleted due to excess hard links, the * fileserver will break the callback promise on the file - if * it had one - before it returns to us, and if it was deleted, * it won't * * however, if we didn't have a callback promise outstanding, * or it was outstanding on a different server, then it won't * break it either... */ vnode = AFS_FS_I(d_inode(dentry)); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) _debug("AFS_VNODE_DELETED"); if (test_bit(AFS_VNODE_CB_BROKEN, &vnode->flags)) _debug("AFS_VNODE_CB_BROKEN"); set_bit(AFS_VNODE_CB_BROKEN, &vnode->flags); ret = afs_validate(vnode, key); _debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret); } key_put(key); _leave(" = 0"); return 0; remove_error: key_put(key); error: _leave(" = %d", ret); return ret; }
struct dentry * cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, unsigned int flags) { unsigned int xid; int rc = 0; /* to get around spurious gcc warning, set to zero here */ struct cifs_sb_info *cifs_sb; struct tcon_link *tlink; struct cifs_tcon *pTcon; struct inode *newInode = NULL; char *full_path = NULL; xid = get_xid(); cifs_dbg(FYI, "parent inode = 0x%p name is: %pd and dentry = 0x%p\n", parent_dir_inode, direntry, direntry); /* check whether path exists */ cifs_sb = CIFS_SB(parent_dir_inode->i_sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) { free_xid(xid); return (struct dentry *)tlink; } pTcon = tlink_tcon(tlink); rc = check_name(direntry); if (rc) goto lookup_out; /* can not grab the rename sem here since it would deadlock in the cases (beginning of sys_rename itself) in which we already have the sb rename sem */ full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; goto lookup_out; } if (d_really_is_positive(direntry)) { cifs_dbg(FYI, "non-NULL inode in lookup\n"); } else { cifs_dbg(FYI, "NULL inode in lookup\n"); } cifs_dbg(FYI, "Full path: %s inode = 0x%p\n", full_path, d_inode(direntry)); if (pTcon->unix_ext) { rc = cifs_get_inode_info_unix(&newInode, full_path, parent_dir_inode->i_sb, xid); } else { rc = cifs_get_inode_info(&newInode, full_path, NULL, parent_dir_inode->i_sb, xid, NULL); } if ((rc == 0) && (newInode != NULL)) { d_add(direntry, newInode); /* since paths are not looked up by component - the parent directories are presumed to be good here */ renew_parental_timestamps(direntry); } else if (rc == -ENOENT) { rc = 0; direntry->d_time = jiffies; d_add(direntry, NULL); /* if it was once a directory (but how can we tell?) we could do shrink_dcache_parent(direntry); */ } else if (rc != -EACCES) { cifs_dbg(FYI, "Unexpected lookup error %d\n", rc); /* We special case check for Access Denied - since that is a common return code */ } lookup_out: kfree(full_path); cifs_put_tlink(tlink); free_xid(xid); return ERR_PTR(rc); }