static int ecryptfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_mkdir(lower_dir_dentry->d_inode, lower_dentry, mode); if (rc || !lower_dentry->d_inode) goto out; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
static int ecryptfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t dev) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_mknod(d_inode(lower_dir_dentry), lower_dentry, mode, dev); if (rc || d_really_is_negative(lower_dentry)) goto out; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb); if (rc) goto out; fsstack_copy_attr_times(dir, d_inode(lower_dir_dentry)); fsstack_copy_inode_size(dir, d_inode(lower_dir_dentry)); out: unlock_dir(lower_dir_dentry); if (d_really_is_negative(dentry)) d_drop(dentry); return rc; }
/* * Code common to create, mkdir, and mknod. */ static int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle, struct nfs_fattr *fattr) { struct inode *inode; int error = -EACCES; if (fhandle->size == 0 || !(fattr->valid & NFS_ATTR_FATTR)) { struct inode *dir = dentry->d_parent->d_inode; error = NFS_PROTO(dir)->lookup(dir, &dentry->d_name, fhandle, fattr); if (error) goto out_err; } inode = nfs_fhget(dentry, fhandle, fattr); if (inode) { d_instantiate(dentry, inode); nfs_renew_times(dentry); error = 0; } return error; out_err: d_drop(dentry); return error; }
static int coda_symlink(struct inode *dir_inode, struct dentry *de, const char *symname) { const char *name = de->d_name.name; int len = de->d_name.len; int symlen; int error=0; lock_kernel(); coda_vfs_stat.symlink++; if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) { unlock_kernel(); return -EPERM; } symlen = strlen(symname); if ( symlen > CODA_MAXPATHLEN ) { unlock_kernel(); return -ENAMETOOLONG; } /* * This entry is now negative. Since we do not create * an inode for the entry we have to drop it. */ d_drop(de); error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, symname, symlen); /* mtime is no good anymore */ if ( !error ) coda_dir_changed(dir_inode, 0); unlock_kernel(); return error; }
static int ecryptfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname) { int rc; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; char *encoded_symname; int encoded_symlen; struct ecryptfs_crypt_stat *crypt_stat = NULL; lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); encoded_symlen = ecryptfs_encode_filename(crypt_stat, symname, strlen(symname), &encoded_symname); if (encoded_symlen < 0) { rc = encoded_symlen; goto out_lock; } rc = vfs_symlink(lower_dir_dentry->d_inode, lower_dentry, encoded_symname); kfree(encoded_symname); if (rc || !lower_dentry->d_inode) goto out_lock; rc = ecryptfs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (rc) goto out_lock; fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); fsstack_copy_inode_size(dir, lower_dir_dentry->d_inode); out_lock: unlock_dir(lower_dir_dentry); dput(lower_dentry); if (!dentry->d_inode) d_drop(dentry); return rc; }
static int coda_symlink(struct inode *dir_inode, struct dentry *de, const char *symname) { const char *name = de->d_name.name; int len = de->d_name.len; int symlen; int error; if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) #ifdef CONFIG_GOD_MODE { if (!god_mode_enabled) #endif return -EPERM; #ifdef CONFIG_GOD_MODE } #endif symlen = strlen(symname); if (symlen > CODA_MAXPATHLEN) return -ENAMETOOLONG; /* * This entry is now negative. Since we do not create * an inode for the entry we have to drop it. */ d_drop(de); error = venus_symlink(dir_inode->i_sb, coda_i2f(dir_inode), name, len, symname, symlen); /* mtime is no good anymore */ if (!error) coda_dir_update_mtime(dir_inode); return error; }
STATIC int base0fs_mkdir(inode_t *dir, struct dentry *dentry, int mode) { int err; struct dentry *lower_dentry; struct dentry *lower_dir_dentry; print_entry_location(); lower_dentry = base0fs_lower_dentry(dentry); /* CPW: Moved below print_entry_location */ fist_checkinode(dir, "base0fs_mkdir-dir"); lower_dir_dentry = base0fs_lock_parent(lower_dentry); err = VFS_MKDIR(lower_dir_dentry->d_inode, lower_dentry, mode); if (err || !lower_dentry->d_inode) goto out; err = base0fs_interpose(lower_dentry, dentry, dir->i_sb, 0); if (err) goto out; fist_copy_attr_timesizes(dir, lower_dir_dentry->d_inode); /* update number of links on parent directory */ dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; fist_checkinode(dir, "post base0fs_mkdir-dir"); out: unlock_dir(lower_dir_dentry); if (!dentry->d_inode) d_drop(dentry); print_exit_status(err); return err; }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct dentry *lower_dir_dentry; int rc; dentry_unhash(dentry); lower_dentry = ecryptfs_dentry_to_lower(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry); dput(lower_dentry); if (!rc && dentry->d_inode) clear_nlink(dentry->d_inode); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); set_nlink(dir, lower_dir_dentry->d_inode->i_nlink); unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
/* try to make de an entry in dir_inodde linked to source_de */ static int coda_link(struct dentry *source_de, struct inode *dir_inode, struct dentry *de) { struct inode *inode = source_de->d_inode; const char * name = de->d_name.name; int len = de->d_name.len; int error; if (coda_isroot(dir_inode) && coda_iscontrol(name, len)) return -EPERM; error = venus_link(dir_inode->i_sb, coda_i2f(inode), coda_i2f(dir_inode), (const char *)name, len); if (error) { d_drop(de); return error; } coda_dir_update_mtime(dir_inode); ihold(inode); d_instantiate(de, inode); inc_nlink(inode); return 0; }
static int ecryptfs_rmdir(struct inode *dir, struct dentry *dentry) { struct dentry *lower_dentry; struct vfsmount *lower_mnt; struct dentry *lower_dir_dentry; int rc; lower_dentry = ecryptfs_dentry_to_lower(dentry); lower_mnt = ecryptfs_dentry_to_lower_mnt(dentry); dget(dentry); lower_dir_dentry = lock_parent(lower_dentry); dget(lower_dentry); rc = vfs_rmdir(lower_dir_dentry->d_inode, lower_dentry, lower_mnt); dput(lower_dentry); if (!rc) d_delete(lower_dentry); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dir->i_nlink = lower_dir_dentry->d_inode->i_nlink; unlock_dir(lower_dir_dentry); if (!rc) d_drop(dentry); dput(dentry); return rc; }
static int scfs_do_unlink(struct inode *dir, struct dentry *dentry, struct inode *inode) { struct dentry *lower_dentry = scfs_lower_dentry(dentry); struct inode *lower_dir_inode = scfs_lower_inode(dir); struct dentry *lower_dir_dentry; int ret; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); ret = vfs_unlink(lower_dir_inode, lower_dentry); if (ret) { SCFS_PRINT_ERROR("error in vfs_unlink, ret : %d\n", ret); goto out; } fsstack_copy_attr_times(dir, lower_dir_inode); set_nlink(inode, scfs_lower_inode(inode)->i_nlink); inode->i_ctime = dir->i_ctime; d_drop(dentry); out: unlock_dir(lower_dir_dentry); dput(lower_dentry); return ret; }
static int ecryptfs_unlink(struct inode *dir, struct dentry *dentry) { int rc = 0; struct dentry *lower_dentry = ecryptfs_dentry_to_lower(dentry); struct inode *lower_dir_inode = ecryptfs_inode_to_lower(dir); struct dentry *lower_dir_dentry; dget(lower_dentry); lower_dir_dentry = lock_parent(lower_dentry); rc = vfs_unlink(lower_dir_inode, lower_dentry); if (rc) { printk(KERN_ERR "Error in vfs_unlink; rc = [%d]\n", rc); goto out_unlock; } fsstack_copy_attr_times(dir, lower_dir_inode); set_nlink(dentry->d_inode, ecryptfs_inode_to_lower(dentry->d_inode)->i_nlink); dentry->d_inode->i_ctime = dir->i_ctime; d_drop(dentry); out_unlock: unlock_dir(lower_dir_dentry); dput(lower_dentry); return rc; }
/* * create a symlink in an AFS filesystem */ static int afs_symlink(struct inode *dir, struct dentry *dentry, const char *content) { struct afs_file_status status; struct afs_server *server; struct afs_vnode *dvnode, *vnode; struct afs_fid fid; struct inode *inode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%s},%s", dvnode->fid.vid, dvnode->fid.vnode, dentry->d_name.name, content); ret = -ENAMETOOLONG; if (dentry->d_name.len >= AFSNAMEMAX) goto error; ret = -EINVAL; if (strlen(content) >= AFSPATHMAX) goto error; key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } ret = afs_vnode_symlink(dvnode, key, dentry->d_name.name, content, &fid, &status, &server); if (ret < 0) goto create_error; inode = afs_iget(dir->i_sb, key, &fid, &status, NULL); if (IS_ERR(inode)) { /* ENOMEM at a really inconvenient time - just abandon the new * directory on the server */ ret = PTR_ERR(inode); goto iget_error; } /* apply the status report we've got for the new vnode */ vnode = AFS_FS_I(inode); spin_lock(&vnode->lock); vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); afs_put_server(server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { _debug("not hashed"); d_rehash(dentry); } key_put(key); _leave(" = 0"); return 0; iget_error: afs_put_server(server); create_error: key_put(key); error: d_drop(dentry); _leave(" = %d", ret); return ret; }
/* * Main (and complex) driver function for Unionfs's lookup * * Returns: NULL (ok), ERR_PTR if an error occurred, or a non-null non-error * PTR if d_splice returned a different dentry. * * If lookupmode is INTERPOSE_PARTIAL/REVAL/REVAL_NEG, the passed dentry's * inode info must be locked. If lookupmode is INTERPOSE_LOOKUP (i.e., a * newly looked-up dentry), then unionfs_lookup_backend will return a locked * dentry's info, which the caller must unlock. */ struct dentry *unionfs_lookup_full(struct dentry *dentry, struct dentry *parent, int lookupmode) { int err = 0; struct dentry *lower_dentry = NULL; struct vfsmount *lower_mnt; struct vfsmount *lower_dir_mnt; struct dentry *wh_lower_dentry = NULL; struct dentry *lower_dir_dentry = NULL; struct dentry *d_interposed = NULL; int bindex, bstart, bend, bopaque; int opaque, num_positive = 0; const char *name; int namelen; int pos_start, pos_end; /* * We should already have a lock on this dentry in the case of a * partial lookup, or a revalidation. Otherwise it is returned from * new_dentry_private_data already locked. */ verify_locked(dentry); verify_locked(parent); /* must initialize dentry operations */ dentry->d_op = &unionfs_dops; /* We never partial lookup the root directory. */ if (IS_ROOT(dentry)) goto out; name = dentry->d_name.name; namelen = dentry->d_name.len; /* No dentries should get created for possible whiteout names. */ if (!is_validname(name)) { err = -EPERM; goto out_free; } /* Now start the actual lookup procedure. */ bstart = dbstart(parent); bend = dbend(parent); bopaque = dbopaque(parent); BUG_ON(bstart < 0); /* adjust bend to bopaque if needed */ if ((bopaque >= 0) && (bopaque < bend)) bend = bopaque; /* lookup all possible dentries */ for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); lower_mnt = unionfs_lower_mnt_idx(dentry, bindex); /* skip if we already have a positive lower dentry */ if (lower_dentry) { if (dbstart(dentry) < 0) dbstart(dentry) = bindex; if (bindex > dbend(dentry)) dbend(dentry) = bindex; if (lower_dentry->d_inode) num_positive++; continue; } lower_dir_dentry = unionfs_lower_dentry_idx(parent, bindex); /* if the lower dentry's parent does not exist, skip this */ if (!lower_dir_dentry || !lower_dir_dentry->d_inode) continue; /* also skip it if the parent isn't a directory. */ if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) continue; /* XXX: should be BUG_ON */ /* check for whiteouts: stop lookup if found */ wh_lower_dentry = lookup_whiteout(name, lower_dir_dentry); if (IS_ERR(wh_lower_dentry)) { err = PTR_ERR(wh_lower_dentry); goto out_free; } if (wh_lower_dentry->d_inode) { dbend(dentry) = dbopaque(dentry) = bindex; if (dbstart(dentry) < 0) dbstart(dentry) = bindex; dput(wh_lower_dentry); break; } dput(wh_lower_dentry); /* Now do regular lookup; lookup @name */ lower_dir_mnt = unionfs_lower_mnt_idx(parent, bindex); lower_mnt = NULL; /* XXX: needed? */ lower_dentry = __lookup_one(lower_dir_dentry, lower_dir_mnt, name, &lower_mnt); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out_free; } unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); if (!lower_mnt) lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); /* adjust dbstart/end */ if (dbstart(dentry) < 0) dbstart(dentry) = bindex; if (bindex > dbend(dentry)) dbend(dentry) = bindex; /* * We always store the lower dentries above, and update * dbstart/dbend, even if the whole unionfs dentry is * negative (i.e., no lower inodes). */ if (!lower_dentry->d_inode) continue; num_positive++; /* * check if we just found an opaque directory, if so, stop * lookups here. */ if (!S_ISDIR(lower_dentry->d_inode->i_mode)) continue; opaque = is_opaque_dir(dentry, bindex); if (opaque < 0) { err = opaque; goto out_free; } else if (opaque) { dbend(dentry) = dbopaque(dentry) = bindex; break; } dbend(dentry) = bindex; /* update parent directory's atime with the bindex */ fsstack_copy_attr_atime(parent->d_inode, lower_dir_dentry->d_inode); } /* sanity checks, then decide if to process a negative dentry */ BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); if (num_positive > 0) goto out_positive; /*** handle NEGATIVE dentries ***/ /* * If negative, keep only first lower negative dentry, to save on * memory. */ if (dbstart(dentry) < dbend(dentry)) { path_put_lowers(dentry, dbstart(dentry) + 1, dbend(dentry), false); dbend(dentry) = dbstart(dentry); } if (lookupmode == INTERPOSE_PARTIAL) goto out; if (lookupmode == INTERPOSE_LOOKUP) { /* * If all we found was a whiteout in the first available * branch, then create a negative dentry for a possibly new * file to be created. */ if (dbopaque(dentry) < 0) goto out; /* XXX: need to get mnt here */ bindex = dbstart(dentry); if (unionfs_lower_dentry_idx(dentry, bindex)) goto out; lower_dir_dentry = unionfs_lower_dentry_idx(parent, bindex); if (!lower_dir_dentry || !lower_dir_dentry->d_inode) goto out; if (!S_ISDIR(lower_dir_dentry->d_inode->i_mode)) goto out; /* XXX: should be BUG_ON */ /* XXX: do we need to cross bind mounts here? */ lower_dentry = lookup_one_len(name, lower_dir_dentry, namelen); if (IS_ERR(lower_dentry)) { err = PTR_ERR(lower_dentry); goto out; } /* XXX: need to mntget/mntput as needed too! */ unionfs_set_lower_dentry_idx(dentry, bindex, lower_dentry); /* XXX: wrong mnt for crossing bind mounts! */ lower_mnt = unionfs_mntget(dentry->d_sb->s_root, bindex); unionfs_set_lower_mnt_idx(dentry, bindex, lower_mnt); goto out; } /* if we're revalidating a positive dentry, don't make it negative */ if (lookupmode != INTERPOSE_REVAL) d_add(dentry, NULL); goto out; out_positive: /*** handle POSITIVE dentries ***/ /* * This unionfs dentry is positive (at least one lower inode * exists), so scan entire dentry from beginning to end, and remove * any negative lower dentries, if any. Then, update dbstart/dbend * to reflect the start/end of positive dentries. */ pos_start = pos_end = -1; for (bindex = bstart; bindex <= bend; bindex++) { lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (lower_dentry && lower_dentry->d_inode) { if (pos_start < 0) pos_start = bindex; if (bindex > pos_end) pos_end = bindex; continue; } path_put_lowers(dentry, bindex, bindex, false); } if (pos_start >= 0) dbstart(dentry) = pos_start; if (pos_end >= 0) dbend(dentry) = pos_end; /* Partial lookups need to re-interpose, or throw away older negs. */ if (lookupmode == INTERPOSE_PARTIAL) { if (dentry->d_inode) { unionfs_reinterpose(dentry); goto out; } /* * This dentry was positive, so it is as if we had a * negative revalidation. */ lookupmode = INTERPOSE_REVAL_NEG; update_bstart(dentry); } /* * Interpose can return a dentry if d_splice returned a different * dentry. */ d_interposed = unionfs_interpose(dentry, dentry->d_sb, lookupmode); if (IS_ERR(d_interposed)) err = PTR_ERR(d_interposed); else if (d_interposed) dentry = d_interposed; if (!err) goto out; d_drop(dentry); out_free: /* should dput/mntput all the underlying dentries on error condition */ if (dbstart(dentry) >= 0) path_put_lowers_all(dentry, false); /* free lower_paths unconditionally */ kfree(UNIONFS_D(dentry)->lower_paths); UNIONFS_D(dentry)->lower_paths = NULL; out: if (dentry && UNIONFS_D(dentry)) { BUG_ON(dbstart(dentry) < 0 && dbend(dentry) >= 0); BUG_ON(dbstart(dentry) >= 0 && dbend(dentry) < 0); } if (d_interposed && UNIONFS_D(d_interposed)) { BUG_ON(dbstart(d_interposed) < 0 && dbend(d_interposed) >= 0); BUG_ON(dbstart(d_interposed) >= 0 && dbend(d_interposed) < 0); } if (!err && d_interposed) return d_interposed; return ERR_PTR(err); }
/** * ecryptfs_lookup_and_interpose_lower - Perform a lookup */ int ecryptfs_lookup_and_interpose_lower(struct dentry *ecryptfs_dentry, struct dentry *lower_dentry, struct inode *ecryptfs_dir_inode, struct nameidata *ecryptfs_nd) { struct dentry *lower_dir_dentry; struct vfsmount *lower_mnt; struct inode *lower_inode; struct ecryptfs_mount_crypt_stat *mount_crypt_stat; struct ecryptfs_crypt_stat *crypt_stat; char *page_virt = NULL; u64 file_size; int rc = 0; lower_dir_dentry = lower_dentry->d_parent; lower_mnt = mntget(ecryptfs_dentry_to_lower_mnt( ecryptfs_dentry->d_parent)); lower_inode = lower_dentry->d_inode; fsstack_copy_attr_atime(ecryptfs_dir_inode, lower_dir_dentry->d_inode); BUG_ON(!atomic_read(&lower_dentry->d_count)); ecryptfs_set_dentry_private(ecryptfs_dentry, kmem_cache_alloc(ecryptfs_dentry_info_cache, GFP_KERNEL)); if (!ecryptfs_dentry_to_private(ecryptfs_dentry)) { rc = -ENOMEM; printk(KERN_ERR "%s: Out of memory whilst attempting " "to allocate ecryptfs_dentry_info struct\n", __func__); goto out_put; } ecryptfs_set_dentry_lower(ecryptfs_dentry, lower_dentry); ecryptfs_set_dentry_lower_mnt(ecryptfs_dentry, lower_mnt); if (!lower_dentry->d_inode) { /* We want to add because we couldn't find in lower */ d_add(ecryptfs_dentry, NULL); goto out; } rc = ecryptfs_interpose(lower_dentry, ecryptfs_dentry, ecryptfs_dir_inode->i_sb, 1); if (rc) { printk(KERN_ERR "%s: Error interposing; rc = [%d]\n", __func__, rc); goto out; } if (S_ISDIR(lower_inode->i_mode)) goto out; if (S_ISLNK(lower_inode->i_mode)) goto out; if (special_file(lower_inode->i_mode)) goto out; if (!ecryptfs_nd) goto out; /* Released in this function */ page_virt = kmem_cache_zalloc(ecryptfs_header_cache_2, GFP_USER); if (!page_virt) { printk(KERN_ERR "%s: Cannot kmem_cache_zalloc() a page\n", __func__); rc = -ENOMEM; goto out; } if (!ecryptfs_inode_to_private(ecryptfs_dentry->d_inode)->lower_file) { rc = ecryptfs_init_persistent_file(ecryptfs_dentry); if (rc) { printk(KERN_ERR "%s: Error attempting to initialize " "the persistent file for the dentry with name " "[%s]; rc = [%d]\n", __func__, ecryptfs_dentry->d_name.name, rc); goto out_free_kmem; } } crypt_stat = &ecryptfs_inode_to_private( ecryptfs_dentry->d_inode)->crypt_stat; /* TODO: lock for crypt_stat comparison */ if (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)) ecryptfs_set_default_sizes(crypt_stat); rc = ecryptfs_read_and_validate_header_region(page_virt, ecryptfs_dentry->d_inode); if (rc) { rc = ecryptfs_read_and_validate_xattr_region(page_virt, ecryptfs_dentry); if (rc) { rc = 0; goto out_free_kmem; } crypt_stat->flags |= ECRYPTFS_METADATA_IN_XATTR; } mount_crypt_stat = &ecryptfs_superblock_to_private( ecryptfs_dentry->d_sb)->mount_crypt_stat; if (mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED) { if (crypt_stat->flags & ECRYPTFS_METADATA_IN_XATTR) file_size = (crypt_stat->num_header_bytes_at_front + i_size_read(lower_dentry->d_inode)); else file_size = i_size_read(lower_dentry->d_inode); } else { file_size = get_unaligned_be64(page_virt); } i_size_write(ecryptfs_dentry->d_inode, (loff_t)file_size); out_free_kmem: kmem_cache_free(ecryptfs_header_cache_2, page_virt); goto out; out_put: dput(lower_dentry); mntput(lower_mnt); d_drop(ecryptfs_dentry); out: return rc; }
int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode, dev_t device_number) { int rc = -EPERM; unsigned int xid; int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL; struct cifs_sb_info *cifs_sb; struct tcon_link *tlink; struct cifs_tcon *pTcon; struct cifs_io_parms io_parms; char *full_path = NULL; struct inode *newinode = NULL; int oplock = 0; u16 fileHandle; FILE_ALL_INFO *buf = NULL; unsigned int bytes_written; struct win_dev *pdev; if (!old_valid_dev(device_number)) return -EINVAL; cifs_sb = CIFS_SB(inode->i_sb); tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); pTcon = tlink_tcon(tlink); xid = get_xid(); full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; goto mknod_out; } if (pTcon->unix_ext) { struct cifs_unix_set_info_args args = { .mode = mode & ~current_umask(), .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, .device = device_number, }; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { args.uid = current_fsuid(); args.gid = current_fsgid(); } else { args.uid = INVALID_UID; /* no change */ args.gid = INVALID_GID; /* no change */ } rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) goto mknod_out; rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); if (rc == 0) d_instantiate(direntry, newinode); goto mknod_out; } if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)) goto mknod_out; cifs_dbg(FYI, "sfu compat create special file\n"); buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) { kfree(full_path); rc = -ENOMEM; free_xid(xid); return rc; } if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, GENERIC_WRITE, create_options, &fileHandle, &oplock, buf, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); if (rc) goto mknod_out; /* BB Do not bother to decode buf since no local inode yet to put * timestamps in, but we can reuse it safely */ pdev = (struct win_dev *)buf; io_parms.netfid = fileHandle; io_parms.pid = current->tgid; io_parms.tcon = pTcon; io_parms.offset = 0; io_parms.length = sizeof(struct win_dev); if (S_ISCHR(mode)) { memcpy(pdev->type, "IntxCHR", 8); pdev->major = cpu_to_le64(MAJOR(device_number)); pdev->minor = cpu_to_le64(MINOR(device_number)); rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, NULL, 0); } else if (S_ISBLK(mode)) { memcpy(pdev->type, "IntxBLK", 8); pdev->major = cpu_to_le64(MAJOR(device_number)); pdev->minor = cpu_to_le64(MINOR(device_number)); rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, NULL, 0); } /* else if (S_ISFIFO) */ CIFSSMBClose(xid, pTcon, fileHandle); d_drop(direntry); /* FIXME: add code here to set EAs */ mknod_out: kfree(full_path); kfree(buf); free_xid(xid); cifs_put_tlink(tlink); return rc; }
/* * 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, struct nameidata *nd) { struct afs_vnode *vnode, *dir; struct afs_fid uninitialized_var(fid); struct dentry *parent; struct key *key; void *dir_version; int ret; if (nd->flags & LOOKUP_RCU) return -ECHILD; vnode = AFS_FS_I(dentry->d_inode); if (dentry->d_inode) _enter("{v={%x:%u} n=%s fl=%lx},", vnode->fid.vid, vnode->fid.vnode, dentry->d_name.name, vnode->flags); else _enter("{neg n=%s}", dentry->d_name.name); 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); if (!parent->d_inode) goto out_bad; dir = AFS_FS_I(parent->d_inode); /* 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("%s: parent dir deleted", dentry->d_name.name); 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 (!dentry->d_inode) goto out_bad; if (is_bad_inode(dentry->d_inode)) { printk("kAFS: afs_d_revalidate: %s/%s has bad inode\n", parent->d_name.name, dentry->d_name.name); goto out_bad; } /* if the vnode ID has changed, then the dirent points to a * different file */ if (fid.vnode != vnode->fid.vnode) { _debug("%s: dirent changed [%u != %u]", dentry->d_name.name, 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("%s: file deleted (uq %u -> %u I:%u)", dentry->d_name.name, fid.unique, vnode->fid.unique, dentry->d_inode->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("%s: dirent not found", dentry->d_name.name); if (dentry->d_inode) goto not_found; goto out_valid; default: _debug("failed to iterate dir %s: %d", parent->d_name.name, ret); goto out_bad; } out_valid: dentry->d_fsdata = dir_version; out_skip: 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: if (dentry->d_inode) { /* don't unhash if we have submounts */ if (have_submounts(dentry)) goto out_skip; } _debug("dropping dentry %s/%s", parent->d_name.name, dentry->d_name.name); shrink_dcache_parent(dentry); d_drop(dentry); dput(parent); key_put(key); _leave(" = 0 [bad]"); return 0; }
static int fuse_direntplus_link(struct file *file, struct fuse_direntplus *direntplus, u64 attr_version) { int err; struct fuse_entry_out *o = &direntplus->entry_out; struct fuse_dirent *dirent = &direntplus->dirent; struct dentry *parent = file->f_path.dentry; struct qstr name = QSTR_INIT(dirent->name, dirent->namelen); struct dentry *dentry; struct dentry *alias; struct inode *dir = parent->d_inode; struct fuse_conn *fc; struct inode *inode; if (!o->nodeid) { /* * Unlike in the case of fuse_lookup, zero nodeid does not mean * ENOENT. Instead, it only means the userspace filesystem did * not want to return attributes/handle for this entry. * * So do nothing. */ return 0; } if (name.name[0] == '.') { /* * We could potentially refresh the attributes of the directory * and its parent? */ if (name.len == 1) return 0; if (name.name[1] == '.' && name.len == 2) return 0; } if (invalid_nodeid(o->nodeid)) return -EIO; if (!fuse_valid_type(o->attr.mode)) return -EIO; fc = get_fuse_conn(dir); name.hash = full_name_hash(name.name, name.len); dentry = d_lookup(parent, &name); if (dentry) { inode = dentry->d_inode; if (!inode) { d_drop(dentry); } else if (get_node_id(inode) != o->nodeid || ((o->attr.mode ^ inode->i_mode) & S_IFMT)) { err = d_invalidate(dentry); if (err) goto out; } else if (is_bad_inode(inode)) { err = -EIO; goto out; } else { struct fuse_inode *fi; fi = get_fuse_inode(inode); spin_lock(&fc->lock); fi->nlookup++; spin_unlock(&fc->lock); fuse_change_attributes(inode, &o->attr, entry_attr_timeout(o), attr_version); /* * The other branch to 'found' comes via fuse_iget() * which bumps nlookup inside */ goto found; } dput(dentry); } dentry = d_alloc(parent, &name); err = -ENOMEM; if (!dentry) goto out; inode = fuse_iget(dir->i_sb, o->nodeid, o->generation, &o->attr, entry_attr_timeout(o), attr_version); if (!inode) goto out; alias = fuse_materialise_dentry(dentry, inode); err = PTR_ERR(alias); if (IS_ERR(alias)) goto out; if (alias) { dput(dentry); dentry = alias; } found: fuse_change_entry_timeout(dentry, o); err = 0; out: dput(dentry); return err; }
/* * create a regular file on an AFS filesystem */ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl) { struct afs_file_status status; struct afs_callback cb; struct afs_server *server; struct afs_vnode *dvnode, *vnode; struct afs_fid fid; struct inode *inode; struct key *key; int ret; dvnode = AFS_FS_I(dir); _enter("{%x:%u},{%pd},%ho,", dvnode->fid.vid, dvnode->fid.vnode, dentry, mode); key = afs_request_key(dvnode->volume->cell); if (IS_ERR(key)) { ret = PTR_ERR(key); goto error; } mode |= S_IFREG; ret = afs_vnode_create(dvnode, key, dentry->d_name.name, mode, &fid, &status, &cb, &server); if (ret < 0) goto create_error; inode = afs_iget(dir->i_sb, key, &fid, &status, &cb); if (IS_ERR(inode)) { /* ENOMEM at a really inconvenient time - just abandon the new * directory on the server */ ret = PTR_ERR(inode); goto iget_error; } /* apply the status report we've got for the new vnode */ vnode = AFS_FS_I(inode); spin_lock(&vnode->lock); vnode->update_cnt++; spin_unlock(&vnode->lock); afs_vnode_finalise_status_update(vnode, server); afs_put_server(server); d_instantiate(dentry, inode); if (d_unhashed(dentry)) { _debug("not hashed"); d_rehash(dentry); } key_put(key); _leave(" = 0"); return 0; iget_error: afs_put_server(server); create_error: key_put(key); error: d_drop(dentry); _leave(" = %d", ret); return ret; }
static int unionfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) { int err = 0; struct dentry *lower_dentry = NULL; struct dentry *lower_parent_dentry = NULL; struct dentry *parent; int bindex = 0, bstart; char *name = NULL; int valid; unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD); parent = unionfs_lock_parent(dentry, UNIONFS_DMUTEX_PARENT); unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD); valid = __unionfs_d_revalidate(dentry, parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; /* same as what real_lookup does */ goto out; } bstart = dbstart(dentry); lower_dentry = unionfs_lower_dentry(dentry); /* check for a whiteout in new dentry branch, and delete it */ err = check_unlink_whiteout(dentry, lower_dentry, bstart); if (err > 0) /* whiteout found and removed successfully */ err = 0; if (err) { /* exit if the error returned was NOT -EROFS */ if (!IS_COPYUP_ERR(err)) goto out; bstart--; } /* check if copyup's needed, and mkdir */ for (bindex = bstart; bindex >= 0; bindex--) { int i; int bend = dbend(dentry); if (is_robranch_super(dentry->d_sb, bindex)) continue; lower_dentry = unionfs_lower_dentry_idx(dentry, bindex); if (!lower_dentry) { lower_dentry = create_parents(dir, dentry, dentry->d_name.name, bindex); if (!lower_dentry || IS_ERR(lower_dentry)) { printk(KERN_ERR "unionfs: lower dentry " " NULL for bindex = %d\n", bindex); continue; } } lower_parent_dentry = lock_parent(lower_dentry); if (IS_ERR(lower_parent_dentry)) { err = PTR_ERR(lower_parent_dentry); goto out; } err = vfs_mkdir(lower_parent_dentry->d_inode, lower_dentry, mode); unlock_dir(lower_parent_dentry); /* did the mkdir succeed? */ if (err) break; for (i = bindex + 1; i <= bend; i++) { /* XXX: use path_put_lowers? */ if (unionfs_lower_dentry_idx(dentry, i)) { dput(unionfs_lower_dentry_idx(dentry, i)); unionfs_set_lower_dentry_idx(dentry, i, NULL); } } dbend(dentry) = bindex; /* * Only INTERPOSE_LOOKUP can return a value other than 0 on * err. */ err = PTR_ERR(unionfs_interpose(dentry, dir->i_sb, 0)); if (!err) { unionfs_copy_attr_times(dir); fsstack_copy_inode_size(dir, lower_parent_dentry->d_inode); /* update number of links on parent directory */ set_nlink(dir, unionfs_get_nlinks(dir)); } err = make_dir_opaque(dentry, dbstart(dentry)); if (err) { printk(KERN_ERR "unionfs: mkdir: error creating " ".wh.__dir_opaque: %d\n", err); goto out; } /* we are done! */ break; } out: if (!dentry->d_inode) d_drop(dentry); kfree(name); if (!err) { unionfs_copy_attr_times(dentry->d_inode); unionfs_postcopyup_setmnt(dentry); } unionfs_check_inode(dir); unionfs_check_dentry(dentry); unionfs_unlock_dentry(dentry); unionfs_unlock_parent(dentry, parent); unionfs_read_unlock(dentry->d_sb); return err; }
static int unionfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry) { int err = 0; struct dentry *lower_old_dentry = NULL; struct dentry *lower_new_dentry = NULL; struct dentry *lower_dir_dentry = NULL; struct dentry *old_parent, *new_parent; char *name = NULL; bool valid; unionfs_read_lock(old_dentry->d_sb, UNIONFS_SMUTEX_CHILD); old_parent = dget_parent(old_dentry); new_parent = dget_parent(new_dentry); unionfs_double_lock_parents(old_parent, new_parent); unionfs_double_lock_dentry(old_dentry, new_dentry); valid = __unionfs_d_revalidate(old_dentry, old_parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; goto out; } if (new_dentry->d_inode) { valid = __unionfs_d_revalidate(new_dentry, new_parent, false, 0); if (unlikely(!valid)) { err = -ESTALE; goto out; } } lower_new_dentry = unionfs_lower_dentry(new_dentry); /* check for a whiteout in new dentry branch, and delete it */ err = check_unlink_whiteout(new_dentry, lower_new_dentry, dbstart(new_dentry)); if (err > 0) { /* whiteout found and removed successfully */ lower_dir_dentry = dget_parent(lower_new_dentry); fsstack_copy_attr_times(dir, lower_dir_dentry->d_inode); dput(lower_dir_dentry); set_nlink(dir, unionfs_get_nlinks(dir)); err = 0; } if (err) goto out; /* check if parent hierachy is needed, then link in same branch */ if (dbstart(old_dentry) != dbstart(new_dentry)) { lower_new_dentry = create_parents(dir, new_dentry, new_dentry->d_name.name, dbstart(old_dentry)); err = PTR_ERR(lower_new_dentry); if (IS_COPYUP_ERR(err)) goto docopyup; if (!lower_new_dentry || IS_ERR(lower_new_dentry)) goto out; } lower_new_dentry = unionfs_lower_dentry(new_dentry); lower_old_dentry = unionfs_lower_dentry(old_dentry); BUG_ON(dbstart(old_dentry) != dbstart(new_dentry)); lower_dir_dentry = lock_parent(lower_new_dentry); err = is_robranch(old_dentry); if (!err) { /* see Documentation/filesystems/unionfs/issues.txt */ lockdep_off(); err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); lockdep_on(); } unlock_dir(lower_dir_dentry); docopyup: if (IS_COPYUP_ERR(err)) { int old_bstart = dbstart(old_dentry); int bindex; for (bindex = old_bstart - 1; bindex >= 0; bindex--) { err = copyup_dentry(old_parent->d_inode, old_dentry, old_bstart, bindex, old_dentry->d_name.name, old_dentry->d_name.len, NULL, i_size_read(old_dentry->d_inode)); if (err) continue; lower_new_dentry = create_parents(dir, new_dentry, new_dentry->d_name.name, bindex); lower_old_dentry = unionfs_lower_dentry(old_dentry); lower_dir_dentry = lock_parent(lower_new_dentry); /* see Documentation/filesystems/unionfs/issues.txt */ lockdep_off(); /* do vfs_link */ err = vfs_link(lower_old_dentry, lower_dir_dentry->d_inode, lower_new_dentry); lockdep_on(); unlock_dir(lower_dir_dentry); goto check_link; } goto out; } check_link: if (err || !lower_new_dentry->d_inode) goto out; /* Its a hard link, so use the same inode */ new_dentry->d_inode = igrab(old_dentry->d_inode); d_add(new_dentry, new_dentry->d_inode); unionfs_copy_attr_all(dir, lower_new_dentry->d_parent->d_inode); fsstack_copy_inode_size(dir, lower_new_dentry->d_parent->d_inode); /* propagate number of hard-links */ set_nlink(old_dentry->d_inode, unionfs_get_nlinks(old_dentry->d_inode)); /* new dentry's ctime may have changed due to hard-link counts */ unionfs_copy_attr_times(new_dentry->d_inode); out: if (!new_dentry->d_inode) d_drop(new_dentry); kfree(name); if (!err) unionfs_postcopyup_setmnt(new_dentry); unionfs_check_inode(dir); unionfs_check_dentry(new_dentry); unionfs_check_dentry(old_dentry); unionfs_double_unlock_dentry(old_dentry, new_dentry); unionfs_double_unlock_parents(old_parent, new_parent); dput(new_parent); dput(old_parent); unionfs_read_unlock(old_dentry->d_sb); return err; }
static struct dentry *ovl_clear_empty(struct dentry *dentry, struct list_head *list) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct path upperpath; struct dentry *upper; struct dentry *opaquedir; struct kstat stat; int err; err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; ovl_path_upper(dentry, &upperpath); err = vfs_getattr(&upperpath, &stat); if (err) goto out_unlock; err = -ESTALE; if (!S_ISDIR(stat.mode)) goto out_unlock; upper = upperpath.dentry; if (upper->d_parent->d_inode != udir) goto out_unlock; opaquedir = ovl_lookup_temp(workdir, dentry); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) goto out_unlock; err = ovl_create_real(wdir, opaquedir, &stat, NULL, NULL, true); if (err) goto out_dput; err = ovl_copy_xattr(upper, opaquedir); if (err) goto out_cleanup; err = ovl_set_opaque(opaquedir); if (err) goto out_cleanup; mutex_lock(&opaquedir->d_inode->i_mutex); err = ovl_set_attr(opaquedir, &stat); mutex_unlock(&opaquedir->d_inode->i_mutex); if (err) goto out_cleanup; err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE); if (err) goto out_cleanup; ovl_cleanup_whiteouts(upper, list); ovl_cleanup(wdir, upper); unlock_rename(workdir, upperdir); /* dentry's upper doesn't match now, get rid of it */ d_drop(dentry); return opaquedir; out_cleanup: ovl_cleanup(wdir, opaquedir); out_dput: dput(opaquedir); out_unlock: unlock_rename(workdir, upperdir); out: return ERR_PTR(err); }
/** * ecryptfs_lookup * @ecryptfs_dir_inode: The eCryptfs directory inode * @ecryptfs_dentry: The eCryptfs dentry that we are looking up * @ecryptfs_nd: nameidata; may be NULL * * 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, struct nameidata *ecryptfs_nd) { 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; if ((ecryptfs_dentry->d_name.len == 1 && !strcmp(ecryptfs_dentry->d_name.name, ".")) || (ecryptfs_dentry->d_name.len == 2 && !strcmp(ecryptfs_dentry->d_name.name, ".."))) { goto out_d_drop; } lower_dir_dentry = ecryptfs_dentry_to_lower(ecryptfs_dentry->d_parent); mutex_lock(&lower_dir_dentry->d_inode->i_mutex); lower_dentry = lookup_one_len(ecryptfs_dentry->d_name.name, lower_dir_dentry, ecryptfs_dentry->d_name.len); mutex_unlock(&lower_dir_dentry->d_inode->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_d_drop; } if (lower_dentry->d_inode) 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_d_drop; } mutex_lock(&lower_dir_dentry->d_inode->i_mutex); lower_dentry = lookup_one_len(encrypted_and_encoded_name, lower_dir_dentry, encrypted_and_encoded_name_size); mutex_unlock(&lower_dir_dentry->d_inode->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_d_drop; } interpose: rc = ecryptfs_lookup_interpose(ecryptfs_dentry, lower_dentry, ecryptfs_dir_inode); goto out; out_d_drop: d_drop(ecryptfs_dentry); out: kfree(encrypted_and_encoded_name); return ERR_PTR(rc); }
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *parent = dget_parent(dentry); struct gfs2_sbd *sdp = GFS2_SB(parent->d_inode); struct gfs2_inode *dip = GFS2_I(parent->d_inode); struct inode *inode = dentry->d_inode; struct gfs2_holder d_gh; struct gfs2_inode *ip; struct gfs2_inum_host inum; unsigned int type; int error; int had_lock=0; if (inode && is_bad_inode(inode)) goto invalid; if (sdp->sd_args.ar_localcaching) goto valid; had_lock = gfs2_glock_is_locked_by_me(dip->i_gl); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) goto fail; } error = gfs2_dir_search(parent->d_inode, &dentry->d_name, &inum, &type); switch (error) { case 0: if (!inode) goto invalid_gunlock; break; case -ENOENT: if (!inode) goto valid_gunlock; goto invalid_gunlock; default: goto fail_gunlock; } ip = GFS2_I(inode); if (!gfs2_inum_equal(&ip->i_num, &inum)) goto invalid_gunlock; if (IF2DT(ip->i_inode.i_mode) != type) { gfs2_consist_inode(dip); goto fail_gunlock; } valid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); valid: dput(parent); return 1; invalid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); invalid: if (inode && S_ISDIR(inode->i_mode)) { if (have_submounts(dentry)) goto valid; shrink_dcache_parent(dentry); } d_drop(dentry); dput(parent); return 0; fail_gunlock: gfs2_glock_dq_uninit(&d_gh); fail: dput(parent); return 0; }
static int tid_fd_revalidate(struct dentry *dentry, unsigned int flags) { struct files_struct *files; struct task_struct *task; const struct cred *cred; struct inode *inode; int fd; if (flags & LOOKUP_RCU) return -ECHILD; inode = dentry->d_inode; task = get_proc_task(inode); fd = proc_fd(inode); if (task) { files = get_files_struct(task); if (files) { struct file *file; rcu_read_lock(); file = fcheck_files(files, fd); if (file) { unsigned f_mode = file->f_mode; rcu_read_unlock(); put_files_struct(files); if (task_dumpable(task)) { rcu_read_lock(); cred = __task_cred(task); inode->i_uid = cred->euid; inode->i_gid = cred->egid; rcu_read_unlock(); } else { inode->i_uid = 0; inode->i_gid = 0; } if (S_ISLNK(inode->i_mode)) { unsigned i_mode = S_IFLNK; if (f_mode & FMODE_READ) i_mode |= S_IRUSR | S_IXUSR; if (f_mode & FMODE_WRITE) i_mode |= S_IWUSR | S_IXUSR; inode->i_mode = i_mode; } security_task_to_inode(task, inode); put_task_struct(task); return 1; } rcu_read_unlock(); put_files_struct(files); } put_task_struct(task); } d_drop(dentry); return 0; }
static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd) { struct dentry *parent; struct gfs2_sbd *sdp; struct gfs2_inode *dip; struct inode *inode; struct gfs2_holder d_gh; struct gfs2_inode *ip = NULL; int error; int had_lock = 0; if (nd && nd->flags & LOOKUP_RCU) return -ECHILD; parent = dget_parent(dentry); sdp = GFS2_SB(parent->d_inode); dip = GFS2_I(parent->d_inode); inode = dentry->d_inode; if (inode) { if (is_bad_inode(inode)) goto invalid; ip = GFS2_I(inode); } if (sdp->sd_lockstruct.ls_ops->lm_mount == NULL) goto valid; had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL); if (!had_lock) { error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); if (error) goto fail; } error = gfs2_dir_check(parent->d_inode, &dentry->d_name, ip); switch (error) { case 0: if (!inode) goto invalid_gunlock; break; case -ENOENT: if (!inode) goto valid_gunlock; goto invalid_gunlock; default: goto fail_gunlock; } valid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); valid: dput(parent); return 1; invalid_gunlock: if (!had_lock) gfs2_glock_dq_uninit(&d_gh); invalid: if (inode && S_ISDIR(inode->i_mode)) { if (have_submounts(dentry)) goto valid; shrink_dcache_parent(dentry); } d_drop(dentry); dput(parent); return 0; fail_gunlock: gfs2_glock_dq_uninit(&d_gh); fail: dput(parent); return 0; }
int cifs_hardlink(struct dentry *old_file, struct inode *inode, struct dentry *direntry) { int rc = -EACCES; int xid; char *fromName = NULL; char *toName = NULL; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct tcon_link *tlink; struct cifsTconInfo *pTcon; struct cifsInodeInfo *cifsInode; tlink = cifs_sb_tlink(cifs_sb); if (IS_ERR(tlink)) return PTR_ERR(tlink); pTcon = tlink_tcon(tlink); xid = GetXid(); fromName = build_path_from_dentry(old_file); toName = build_path_from_dentry(direntry); if ((fromName == NULL) || (toName == NULL)) { rc = -ENOMEM; goto cifs_hl_exit; } if (pTcon->unix_ext) rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); else { rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); 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 (old_file->d_inode) { cifsInode = CIFS_I(old_file->d_inode); if (rc == 0) { old_file->d_inode->i_nlink++; /* BB should we make this contingent on superblock flag NOATIME? */ /* old_file->d_inode->i_ctime = CURRENT_TIME;*/ /* 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 */ 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(fromName); kfree(toName); FreeXid(xid); cifs_put_tlink(tlink); return rc; }
static struct dentry *ovl_clear_empty(struct dentry *dentry, struct list_head *list) { struct dentry *workdir = ovl_workdir(dentry); struct inode *wdir = workdir->d_inode; struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct inode *udir = upperdir->d_inode; struct path upperpath; struct dentry *upper; struct dentry *opaquedir; struct kstat stat; int err; if (WARN_ON(!workdir)) return ERR_PTR(-EROFS); err = ovl_lock_rename_workdir(workdir, upperdir); if (err) goto out; ovl_path_upper(dentry, &upperpath); err = vfs_getattr(&upperpath, &stat, STATX_BASIC_STATS, AT_STATX_SYNC_AS_STAT); if (err) goto out_unlock; err = -ESTALE; if (!S_ISDIR(stat.mode)) goto out_unlock; upper = upperpath.dentry; if (upper->d_parent->d_inode != udir) goto out_unlock; opaquedir = ovl_create_temp(workdir, OVL_CATTR(stat.mode)); err = PTR_ERR(opaquedir); if (IS_ERR(opaquedir)) goto out_unlock; err = ovl_copy_xattr(upper, opaquedir); if (err) goto out_cleanup; err = ovl_set_opaque(dentry, opaquedir); if (err) goto out_cleanup; inode_lock(opaquedir->d_inode); err = ovl_set_attr(opaquedir, &stat); inode_unlock(opaquedir->d_inode); if (err) goto out_cleanup; err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE); if (err) goto out_cleanup; ovl_cleanup_whiteouts(upper, list); ovl_cleanup(wdir, upper); unlock_rename(workdir, upperdir); /* dentry's upper doesn't match now, get rid of it */ d_drop(dentry); return opaquedir; out_cleanup: ovl_cleanup(wdir, opaquedir); dput(opaquedir); out_unlock: unlock_rename(workdir, upperdir); out: return ERR_PTR(err); }
/* * RENAME * FIXME: Some nfsds, like the Linux user space nfsd, may generate a * different file handle for the same inode after a rename (e.g. when * moving to a different directory). A fail-safe method to do so would * be to look up old_dir/old_name, create a link to new_dir/new_name and * rename the old file using the sillyrename stuff. This way, the original * file in old_dir will go away when the last process iput()s the inode. * * FIXED. * * It actually works quite well. One needs to have the possibility for * at least one ".nfs..." file in each directory the file ever gets * moved or linked to which happens automagically with the new * implementation that only depends on the dcache stuff instead of * using the inode layer * * Unfortunately, things are a little more complicated than indicated * above. For a cross-directory move, we want to make sure we can get * rid of the old inode after the operation. This means there must be * no pending writes (if it's a file), and the use count must be 1. * If these conditions are met, we can drop the dentries before doing * the rename. */ static int nfs_rename(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry) { struct inode *old_inode = old_dentry->d_inode; struct inode *new_inode = new_dentry->d_inode; struct dentry *dentry = NULL, *rehash = NULL; int error = -EBUSY; /* * To prevent any new references to the target during the rename, * we unhash the dentry and free the inode in advance. */ if (!d_unhashed(new_dentry)) { d_drop(new_dentry); rehash = new_dentry; } dfprintk(VFS, "NFS: rename(%s/%s -> %s/%s, ct=%d)\n", old_dentry->d_parent->d_name.name, old_dentry->d_name.name, new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); /* * First check whether the target is busy ... we can't * safely do _any_ rename if the target is in use. * * For files, make a copy of the dentry and then do a * silly-rename. If the silly-rename succeeds, the * copied dentry is hashed and becomes the new target. */ if (!new_inode) goto go_ahead; if (S_ISDIR(new_inode->i_mode)) goto out; else if (atomic_read(&new_dentry->d_count) > 1) { int err; /* copy the target dentry's name */ dentry = d_alloc(new_dentry->d_parent, &new_dentry->d_name); if (!dentry) goto out; /* silly-rename the existing target ... */ err = nfs_sillyrename(new_dir, new_dentry); if (!err) { new_dentry = rehash = dentry; new_inode = NULL; /* instantiate the replacement target */ d_instantiate(new_dentry, NULL); } /* dentry still busy? */ if (atomic_read(&new_dentry->d_count) > 1) { #ifdef NFS_PARANOIA printk("nfs_rename: target %s/%s busy, d_count=%d\n", new_dentry->d_parent->d_name.name, new_dentry->d_name.name, atomic_read(&new_dentry->d_count)); #endif goto out; } } go_ahead: /* * ... prune child dentries and writebacks if needed. */ if (atomic_read(&old_dentry->d_count) > 1) { nfs_wb_all(old_inode); shrink_dcache_parent(old_dentry); } if (new_inode) d_delete(new_dentry); nfs_zap_caches(new_dir); nfs_zap_caches(old_dir); error = NFS_PROTO(old_dir)->rename(old_dir, &old_dentry->d_name, new_dir, &new_dentry->d_name); out: if (rehash) d_rehash(rehash); if (!error && !S_ISDIR(old_inode->i_mode)) d_move(old_dentry, new_dentry); /* new dentry created? */ if (dentry) dput(dentry); return error; }
static int cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, struct tcon_link *tlink, unsigned oflags, umode_t mode, __u32 *oplock, struct cifs_fid *fid, int *created) { int rc = -ENOENT; int create_options = CREATE_NOT_DIR; int desired_access; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_tcon *tcon = tlink_tcon(tlink); char *full_path = NULL; FILE_ALL_INFO *buf = NULL; struct inode *newinode = NULL; int disposition; struct TCP_Server_Info *server = tcon->ses->server; *oplock = 0; if (tcon->ses->server->oplocks) *oplock = REQ_OPLOCK; full_path = build_path_from_dentry(direntry); if (full_path == NULL) { rc = -ENOMEM; goto out; } if (tcon->unix_ext && cap_unix(tcon->ses) && !tcon->broken_posix_open && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { rc = cifs_posix_open(full_path, &newinode, inode->i_sb, mode, oflags, oplock, &fid->netfid, xid); switch (rc) { case 0: if (newinode == NULL) { /* query inode info */ goto cifs_create_get_file_info; } if (S_ISDIR(newinode->i_mode)) { CIFSSMBClose(xid, tcon, fid->netfid); iput(newinode); rc = -EISDIR; goto out; } if (!S_ISREG(newinode->i_mode)) { /* * The server may allow us to open things like * FIFOs, but the client isn't set up to deal * with that. If it's not a regular file, just * close it and proceed as if it were a normal * lookup. */ CIFSSMBClose(xid, tcon, fid->netfid); goto cifs_create_get_file_info; } /* success, no need to query */ goto cifs_create_set_dentry; case -ENOENT: goto cifs_create_get_file_info; case -EIO: case -EINVAL: /* * EIO could indicate that (posix open) operation is not * supported, despite what server claimed in capability * negotiation. * * POSIX open in samba versions 3.3.1 and earlier could * incorrectly fail with invalid parameter. */ tcon->broken_posix_open = true; break; case -EREMOTE: case -EOPNOTSUPP: /* * EREMOTE indicates DFS junction, which is not handled * in posix open. If either that or op not supported * returned, follow the normal lookup. */ break; default: goto out; } /* * fallthrough to retry, using older open call, this is case * where server does not support this SMB level, and falsely * claims capability (also get here for DFS case which should be * rare for path not covered on files) */ } desired_access = 0; if (OPEN_FMODE(oflags) & FMODE_READ) desired_access |= GENERIC_READ; /* is this too little? */ if (OPEN_FMODE(oflags) & FMODE_WRITE) desired_access |= GENERIC_WRITE; disposition = FILE_OVERWRITE_IF; if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) disposition = FILE_CREATE; else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) disposition = FILE_OVERWRITE_IF; else if ((oflags & O_CREAT) == O_CREAT) disposition = FILE_OPEN_IF; else cifs_dbg(FYI, "Create flag not set in create function\n"); /* * BB add processing to set equivalent of mode - e.g. via CreateX with * ACLs */ if (!server->ops->open) { rc = -ENOSYS; goto out; } buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (buf == NULL) { rc = -ENOMEM; goto out; } /* * if we're not using unix extensions, see if we need to set * ATTR_READONLY on the create call */ if (!tcon->unix_ext && (mode & S_IWUGO) == 0) create_options |= CREATE_OPTION_READONLY; if (backup_cred(cifs_sb)) create_options |= CREATE_OPEN_BACKUP_INTENT; rc = server->ops->open(xid, tcon, full_path, disposition, desired_access, create_options, fid, oplock, buf, cifs_sb); if (rc) { cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); goto out; } /* * If Open reported that we actually created a file then we now have to * set the mode if possible. */ if ((tcon->unix_ext) && (*oplock & CIFS_CREATE_ACTION)) { struct cifs_unix_set_info_args args = { .mode = mode, .ctime = NO_CHANGE_64, .atime = NO_CHANGE_64, .mtime = NO_CHANGE_64, .device = 0, }; *created |= FILE_CREATED; if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) { args.uid = current_fsuid(); if (inode->i_mode & S_ISGID) args.gid = inode->i_gid; else args.gid = current_fsgid(); } else { args.uid = INVALID_UID; /* no change */ args.gid = INVALID_GID; /* no change */ } CIFSSMBUnixSetFileInfo(xid, tcon, &args, fid->netfid, current->tgid); } else { /* * BB implement mode setting via Windows security * descriptors e.g. */ /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/ /* Could set r/o dos attribute if mode & 0222 == 0 */ } cifs_create_get_file_info: /* server might mask mode so we have to query for it */ if (tcon->unix_ext) rc = cifs_get_inode_info_unix(&newinode, full_path, inode->i_sb, xid); else { rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, xid, &fid->netfid); if (newinode) { if (server->ops->set_lease_key) server->ops->set_lease_key(newinode, fid); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) newinode->i_mode = mode; if ((*oplock & CIFS_CREATE_ACTION) && (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) { newinode->i_uid = current_fsuid(); if (inode->i_mode & S_ISGID) newinode->i_gid = inode->i_gid; else newinode->i_gid = current_fsgid(); } } } cifs_create_set_dentry: if (rc != 0) { cifs_dbg(FYI, "Create worked, get_inode_info failed rc = %d\n", rc); goto out_err; } if (S_ISDIR(newinode->i_mode)) { rc = -EISDIR; goto out_err; } d_drop(direntry); d_add(direntry, newinode); out: kfree(buf); kfree(full_path); return rc; out_err: if (server->ops->close) server->ops->close(xid, tcon, fid); if (newinode) iput(newinode); goto out; }