int expkey_parse(struct cache_detail *cd, char *mesg, int mlen) { /* client fsidtype fsid [path] */ char *buf; int len; struct auth_domain *dom = NULL; int err; int fsidtype; char *ep; struct svc_expkey key; if (mesg[mlen-1] != '\n') return -EINVAL; mesg[mlen-1] = 0; buf = kmalloc(PAGE_SIZE, GFP_KERNEL); err = -ENOMEM; if (!buf) goto out; err = -EINVAL; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) goto out; err = -ENOENT; dom = auth_domain_find(buf); if (!dom) goto out; dprintk("found domain %s\n", buf); err = -EINVAL; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) goto out; fsidtype = simple_strtoul(buf, &ep, 10); if (*ep) goto out; dprintk("found fsidtype %d\n", fsidtype); if (fsidtype > 2) goto out; if ((len=qword_get(&mesg, buf, PAGE_SIZE)) <= 0) goto out; dprintk("found fsid length %d\n", len); if (len != key_len(fsidtype)) goto out; /* OK, we seem to have a valid key */ key.h.flags = 0; key.h.expiry_time = get_expiry(&mesg); if (key.h.expiry_time == 0) goto out; key.ek_client = dom; key.ek_fsidtype = fsidtype; memcpy(key.ek_fsid, buf, len); /* now we want a pathname, or empty meaning NEGATIVE */ if ((len=qword_get(&mesg, buf, PAGE_SIZE)) < 0) goto out; dprintk("Path seems to be <%s>\n", buf); err = 0; if (len == 0) { struct svc_expkey *ek; set_bit(CACHE_NEGATIVE, &key.h.flags); ek = svc_expkey_lookup(&key, 1); if (ek) expkey_put(&ek->h, &svc_expkey_cache); } else { struct nameidata nd; struct svc_expkey *ek; struct svc_export *exp; err = path_lookup(buf, 0, &nd); if (err) goto out; dprintk("Found the path %s\n", buf); exp = exp_get_by_name(dom, nd.mnt, nd.dentry, NULL); err = -ENOENT; if (!exp) goto out_nd; key.ek_export = exp; dprintk("And found export\n"); ek = svc_expkey_lookup(&key, 1); if (ek) expkey_put(&ek->h, &svc_expkey_cache); exp_put(exp); err = 0; out_nd: path_release(&nd); } cache_flush(); out: if (dom) auth_domain_put(dom); if (buf) kfree(buf); return err; }
/* * xfs_find_handle maps from userspace xfs_fsop_handlereq structure to * a file or fs handle. * * XFS_IOC_PATH_TO_FSHANDLE * returns fs handle for a mount point or path within that mount point * XFS_IOC_FD_TO_HANDLE * returns full handle for a FD opened in user space * XFS_IOC_PATH_TO_HANDLE * returns full handle for a path */ STATIC int xfs_find_handle( unsigned int cmd, void __user *arg) { int hsize; xfs_handle_t handle; xfs_fsop_handlereq_t hreq; struct inode *inode; struct vnode *vp; if (copy_from_user(&hreq, arg, sizeof(hreq))) return -XFS_ERROR(EFAULT); memset((char *)&handle, 0, sizeof(handle)); switch (cmd) { case XFS_IOC_PATH_TO_FSHANDLE: case XFS_IOC_PATH_TO_HANDLE: { struct nameidata nd; int error; error = user_path_walk_link((const char __user *)hreq.path, &nd); if (error) return error; ASSERT(nd.dentry); ASSERT(nd.dentry->d_inode); inode = igrab(nd.dentry->d_inode); path_release(&nd); break; } case XFS_IOC_FD_TO_HANDLE: { struct file *file; file = fget(hreq.fd); if (!file) return -EBADF; ASSERT(file->f_dentry); ASSERT(file->f_dentry->d_inode); inode = igrab(file->f_dentry->d_inode); fput(file); break; } default: ASSERT(0); return -XFS_ERROR(EINVAL); } if (inode->i_sb->s_magic != XFS_SB_MAGIC) { /* we're not in XFS anymore, Toto */ iput(inode); return -XFS_ERROR(EINVAL); } switch (inode->i_mode & S_IFMT) { case S_IFREG: case S_IFDIR: case S_IFLNK: break; default: iput(inode); return -XFS_ERROR(EBADF); } /* we need the vnode */ vp = vn_from_inode(inode); /* now we can grab the fsid */ memcpy(&handle.ha_fsid, vp->v_vfsp->vfs_altfsid, sizeof(xfs_fsid_t)); hsize = sizeof(xfs_fsid_t); if (cmd != XFS_IOC_PATH_TO_FSHANDLE) { xfs_inode_t *ip; int lock_mode; /* need to get access to the xfs_inode to read the generation */ ip = xfs_vtoi(vp); ASSERT(ip); lock_mode = xfs_ilock_map_shared(ip); /* fill in fid section of handle from inode */ handle.ha_fid.xfs_fid_len = sizeof(xfs_fid_t) - sizeof(handle.ha_fid.xfs_fid_len); handle.ha_fid.xfs_fid_pad = 0; handle.ha_fid.xfs_fid_gen = ip->i_d.di_gen; handle.ha_fid.xfs_fid_ino = ip->i_ino; xfs_iunlock_map_shared(ip, lock_mode); hsize = XFS_HSIZE(handle); } /* now copy our handle into the user buffer & write out the size */ if (copy_to_user(hreq.ohandle, &handle, hsize) || copy_to_user(hreq.ohandlen, &hsize, sizeof(__s32))) { iput(inode); return -XFS_ERROR(EFAULT); } iput(inode); return 0; }
static int jffs2_get_sb(struct file_system_type *fs_type, int flags, const char *dev_name, void *data, struct vfsmount *mnt) { int err; struct nameidata nd; int mtdnr; if (!dev_name) return -EINVAL; D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); /* The preferred way of mounting in future; especially when CONFIG_BLK_DEV is implemented - we specify the underlying MTD device by number or by name, so that we don't require block device support to be present in the kernel. */ /* FIXME: How to do the root fs this way? */ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { /* Probably mounting without the blkdev crap */ if (dev_name[3] == ':') { struct mtd_info *mtd; /* Mount by MTD device name */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { mtd = get_mtd_device(NULL, mtdnr); if (!IS_ERR(mtd)) { if (!strcmp(mtd->name, dev_name+4)) return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd, mnt); put_mtd_device(mtd); } } printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); } else if (isdigit(dev_name[3])) { /* Mount by MTD device number name */ char *endptr; mtdnr = simple_strtoul(dev_name+3, &endptr, 0); if (!*endptr) { /* It was a valid number */ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); } } } /* Try the old way - the hack where we allowed users to mount /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", err, nd.dentry->d_inode)); if (err) return err; err = -EINVAL; if (!S_ISBLK(nd.dentry->d_inode->i_mode)) goto out; if (nd.mnt->mnt_flags & MNT_NODEV) { err = -EACCES; goto out; } if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { if (!(flags & MS_SILENT)) printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", dev_name); goto out; } mtdnr = iminor(nd.dentry->d_inode); path_release(&nd); return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr, mnt); out: path_release(&nd); return err; }
/* * parse the dirs= mount argument * * We don't need to lock the superblock private data's rwsem, as we get * called only by unionfs_read_super - it is still a long time before anyone * can even get a reference to us. */ static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info *lower_root_info, char *options) { struct nameidata nd; char *name; int err = 0; int branches = 1; int bindex = 0; int i = 0; int j = 0; struct dentry *dent1; struct dentry *dent2; if (options[0] == '\0') { printk(KERN_ERR "unionfs: no branches specified\n"); err = -EINVAL; goto out; } /* * Each colon means we have a separator, this is really just a rough * guess, since strsep will handle empty fields for us. */ for (i = 0; options[i]; i++) if (options[i] == ':') branches++; /* allocate space for underlying pointers to lower dentry */ UNIONFS_SB(sb)->data = kcalloc(branches, sizeof(struct unionfs_data), GFP_KERNEL); if (unlikely(!UNIONFS_SB(sb)->data)) { err = -ENOMEM; goto out; } lower_root_info->lower_paths = kcalloc(branches, sizeof(struct path), GFP_KERNEL); if (unlikely(!lower_root_info->lower_paths)) { err = -ENOMEM; goto out; } /* now parsing a string such as "b1:b2=rw:b3=ro:b4" */ branches = 0; while ((name = strsep(&options, ":")) != NULL) { int perms; char *mode = strchr(name, '='); if (!name) continue; if (!*name) { /* bad use of ':' (extra colons) */ err = -EINVAL; goto out; } branches++; /* strip off '=' if any */ if (mode) *mode++ = '\0'; err = parse_branch_mode(mode, &perms); if (err) { printk(KERN_ERR "unionfs: invalid mode \"%s\" for " "branch %d\n", mode, bindex); goto out; } /* ensure that leftmost branch is writeable */ if (!bindex && !(perms & MAY_WRITE)) { printk(KERN_ERR "unionfs: leftmost branch cannot be " "read-only (use \"-o ro\" to create a " "read-only union)\n"); err = -EINVAL; goto out; } err = path_lookup(name, LOOKUP_FOLLOW, &nd); if (err) { printk(KERN_ERR "unionfs: error accessing " "lower directory '%s' (error %d)\n", name, err); goto out; } err = check_branch(&nd); if (err) { printk(KERN_ERR "unionfs: lower directory " "'%s' is not a valid branch\n", name); path_release(&nd); goto out; } lower_root_info->lower_paths[bindex].dentry = nd.dentry; lower_root_info->lower_paths[bindex].mnt = nd.mnt; set_branchperms(sb, bindex, perms); set_branch_count(sb, bindex, 0); new_branch_id(sb, bindex); if (lower_root_info->bstart < 0) lower_root_info->bstart = bindex; lower_root_info->bend = bindex; bindex++; } if (branches == 0) { printk(KERN_ERR "unionfs: no branches specified\n"); err = -EINVAL; goto out; } BUG_ON(branches != (lower_root_info->bend + 1)); /* * Ensure that no overlaps exist in the branches. * * This test is required because the Linux kernel has no support * currently for ensuring coherency between stackable layers and * branches. If we were to allow overlapping branches, it would be * possible, for example, to delete a file via one branch, which * would not be reflected in another branch. Such incoherency could * lead to inconsistencies and even kernel oopses. Rather than * implement hacks to work around some of these cache-coherency * problems, we prevent branch overlapping, for now. A complete * solution will involve proper kernel/VFS support for cache * coherency, at which time we could safely remove this * branch-overlapping test. */ for (i = 0; i < branches; i++) { dent1 = lower_root_info->lower_paths[i].dentry; for (j = i + 1; j < branches; j++) { dent2 = lower_root_info->lower_paths[j].dentry; if (is_branch_overlap(dent1, dent2)) { printk(KERN_ERR "unionfs: branches %d and " "%d overlap\n", i, j); err = -EINVAL; goto out; } } } out: if (err) { for (i = 0; i < branches; i++) if (lower_root_info->lower_paths[i].dentry) { dput(lower_root_info->lower_paths[i].dentry); /* initialize: can't use unionfs_mntput here */ mntput(lower_root_info->lower_paths[i].mnt); } kfree(lower_root_info->lower_paths); kfree(UNIONFS_SB(sb)->data); /* * MUST clear the pointers to prevent potential double free if * the caller dies later on */ lower_root_info->lower_paths = NULL; UNIONFS_SB(sb)->data = NULL; } return err; }
/* If times==NULL, set access and modification to current time, * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ long do_utimes(int dfd, char __user *filename, struct timespec *times, int flags) { int error; struct nameidata nd; struct dentry *dentry; struct inode *inode; struct iattr newattrs; struct file *f = NULL; error = -EINVAL; if (times && (!nsec_valid(times[0].tv_nsec) || !nsec_valid(times[1].tv_nsec))) { goto out; } if (flags & ~AT_SYMLINK_NOFOLLOW) goto out; if (filename == NULL && dfd != AT_FDCWD) { error = -EINVAL; if (flags & AT_SYMLINK_NOFOLLOW) goto out; error = -EBADF; f = fget(dfd); if (!f) goto out; dentry = f->f_path.dentry; } else { error = __user_walk_fd(dfd, filename, (flags & AT_SYMLINK_NOFOLLOW) ? 0 : LOOKUP_FOLLOW, &nd); if (error) goto out; dentry = nd.dentry; } inode = dentry->d_inode; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; /* Don't worry, the checks are done in inode_change_ok() */ newattrs.ia_valid = ATTR_CTIME | ATTR_MTIME | ATTR_ATIME; if (times) { error = -EPERM; if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) goto dput_and_out; if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { newattrs.ia_atime.tv_sec = times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { newattrs.ia_mtime.tv_sec = times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } } else { error = -EACCES; if (IS_IMMUTABLE(inode)) goto dput_and_out; if (!is_owner_or_cap(inode)) { if (f) { if (!(f->f_mode & FMODE_WRITE)) goto dput_and_out; } else { error = vfs_permission(&nd, MAY_WRITE); if (error) goto dput_and_out; } } } mutex_lock(&inode->i_mutex); error = notify_change(dentry, &newattrs); mutex_unlock(&inode->i_mutex); dput_and_out: if (f) fput(f); else path_release(&nd); out: return error; }
static void *cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) { DFS_INFO3_PARAM *referrals = NULL; unsigned int num_referrals = 0; struct cifs_sb_info *cifs_sb; struct cifsSesInfo *ses; char *full_path = NULL; int xid, i; int rc = 0; struct vfsmount *mnt = ERR_PTR(-ENOENT); cFYI(1, ("in %s", __FUNCTION__ )); BUG_ON(IS_ROOT(dentry)); xid = GetXid(); dput(nd->dentry); nd->dentry = dget(dentry); if (d_mountpoint(nd->dentry)) { goto out_follow; } if ( dentry->d_inode == NULL ) { rc = -EINVAL; goto out_err; } cifs_sb = CIFS_SB(dentry->d_inode->i_sb); ses = cifs_sb->tcon->ses; if ( !ses ) { rc = -EINVAL; goto out_err; } full_path = build_full_dfs_path_from_dentry(dentry); if ( full_path == NULL ) { rc = -ENOMEM; goto out_err; } rc = get_dfs_path(xid, ses , full_path, cifs_sb->local_nls, &num_referrals, &referrals, cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); for (i = 0; i < num_referrals; i++) { cFYI(1, ("%s: ref path: %s", __FUNCTION__, referrals[i].path_name)); cFYI(1, ("%s: node path: %s", __FUNCTION__, referrals[i].node_name )); cFYI(1, ("%s: fl: %hd, serv_type: %hd, ref_flags: %hd, " "path_consumed: %hd", __FUNCTION__, referrals[i].flags, referrals[i].server_type, referrals[i].ref_flag, referrals[i].PathConsumed)); /* connect to storage node */ if (referrals[i].flags & DFSREF_STORAGE_SERVER) { int len; len = strlen(referrals[i].node_name); if (len < 2) { cERROR(1, ("%s: Net Address path too short: %s", __FUNCTION__, referrals[i].node_name )); rc = -EINVAL; goto out_err; } else { mnt = cifs_dfs_do_refmount(nd->mnt, nd->dentry, referrals[i].node_name); cFYI(1, ("%s: cifs_dfs_do_refmount:%s , mnt:%p", __FUNCTION__, referrals[i].node_name, mnt)); if ( !rc ) { /* have server so stop here & return */ break; } } } } rc = PTR_ERR(mnt); if (IS_ERR(mnt)) goto out_err; mntget(mnt); rc = do_add_mount(mnt, nd, nd->mnt->mnt_flags, &cifs_dfs_automount_list); if (rc < 0) { mntput(mnt); if (rc == -EBUSY) goto out_follow; goto out_err; } mntput(nd->mnt); dput(nd->dentry); nd->mnt = mnt; nd->dentry = dget(mnt->mnt_root); out: FreeXid(xid); free_dfs_info_array(referrals, num_referrals); cFYI(1, ("leaving %s", __FUNCTION__ )); return ERR_PTR(rc); out_err: if ( full_path ) kfree(full_path); path_release(nd); goto out; out_follow: while (d_mountpoint(nd->dentry) && follow_down(&nd->mnt, &nd->dentry)) ; rc = 0; goto out; }
/* given a path: write a close record and cancel an LML record, finally call truncate LML. Lento is doing this so it goes in with uid/gid's root. */ int lento_cancel_lml(char *path, __u64 lml_offset, __u64 remote_ino, __u32 remote_generation, __u32 remote_version, struct lento_vfs_context *info) { struct nameidata nd; struct rec_info rec; struct dentry *dentry; int error; struct presto_file_set *fset; void *handle; struct presto_version new_ver; ENTRY; error = presto_walk(path, &nd); if (error) { EXIT; return error; } dentry = nd.dentry; error = -ENXIO; if ( !presto_ispresto(dentry->d_inode) ) { EXIT; goto out_cancel_lml; } fset = presto_fset(dentry); error=-EINVAL; if (fset==NULL) { CERROR("No fileset!\n"); EXIT; goto out_cancel_lml; } /* this only requires a transaction below which is automatic */ handle = presto_trans_start(fset, dentry->d_inode, PRESTO_OP_RELEASE); if ( IS_ERR(handle) ) { error = -ENOMEM; EXIT; goto out_cancel_lml; } if (info->flags & LENTO_FL_CANCEL_LML) { error = presto_clear_lml_close(fset, lml_offset); if ( error ) { presto_trans_commit(fset, handle); EXIT; goto out_cancel_lml; } } if (info->flags & LENTO_FL_WRITE_KML) { struct file file; file.private_data = NULL; file.f_dentry = dentry; presto_getversion(&new_ver, dentry->d_inode); error = presto_journal_close(&rec, fset, &file, dentry, &new_ver); if ( error ) { EXIT; presto_trans_commit(fset, handle); goto out_cancel_lml; } } if (info->flags & LENTO_FL_WRITE_EXPECT) { error = presto_write_last_rcvd(&rec, fset, info); if ( error < 0 ) { EXIT; presto_trans_commit(fset, handle); goto out_cancel_lml; } } presto_trans_commit(fset, handle); if (info->flags & LENTO_FL_CANCEL_LML) { presto_truncate_lml(fset); } out_cancel_lml: EXIT; path_release(&nd); return error; }
static noinline_for_stack struct dentry *decode_by_path(struct super_block *sb, aufs_bindex_t bindex, ino_t ino, __u32 *fh, int fh_len, struct au_nfsd_si_lock *nsi_lock) { struct dentry *dentry, *h_parent, *root; struct super_block *h_sb; char *pathname, *p; struct vfsmount *h_mnt; struct au_branch *br; int err; struct nameidata nd; struct path path; LKTRTrace("b%d\n", bindex); SiMustAnyLock(sb); br = au_sbr(sb, bindex); /* au_br_get(br); */ h_mnt = br->br_mnt; h_sb = h_mnt->mnt_sb; LKTRTrace("%s, h_decode_fh\n", au_sbtype(h_sb)); h_parent = au_call_decode_fh(h_mnt, fh + Fh_tail, fh_len - Fh_tail, fh[Fh_h_type], h_acceptable, /*context*/NULL); dentry = h_parent; if (unlikely(!h_parent || IS_ERR(h_parent))) { AuWarn1("%s decode_fh failed, %ld\n", au_sbtype(h_sb), PTR_ERR(h_parent)); goto out; } dentry = NULL; if (unlikely(au_test_anon(h_parent))) { AuWarn1("%s decode_fh returned a disconnected dentry\n", au_sbtype(h_sb)); goto out_h_parent; } dentry = ERR_PTR(-ENOMEM); pathname = (void *)__get_free_page(GFP_NOFS); if (unlikely(!pathname)) goto out_h_parent; root = sb->s_root; path.mnt = h_mnt; di_read_lock_parent(root, !AuLock_IR); path.dentry = au_h_dptr(root, bindex); di_read_unlock(root, !AuLock_IR); p = au_build_path(h_parent, &path, pathname, PAGE_SIZE, sb); dentry = (void *)p; if (IS_ERR(p)) goto out_pathname; LKTRTrace("%s\n", p); si_read_unlock(sb); err = vfsub_path_lookup(p, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, &nd); dentry = ERR_PTR(err); if (unlikely(err)) goto out_relock; dentry = ERR_PTR(-ENOENT); AuDebugOn(au_test_anon(nd.dentry)); if (unlikely(!nd.dentry->d_inode)) goto out_nd; if (ino != nd.dentry->d_inode->i_ino) { path.mnt = nd.mnt; path.dentry = nd.dentry; dentry = au_lkup_by_ino(&path, ino, /*nsi_lock*/NULL); } else dentry = dget(nd.dentry); out_nd: path_release(&nd); out_relock: if (unlikely(si_nfsd_read_lock(sb, nsi_lock) < 0)) if (!IS_ERR(dentry)) { dput(dentry); dentry = ERR_PTR(-ESTALE); } out_pathname: free_page((unsigned long)pathname); out_h_parent: dput(h_parent); out: /* au_br_put(br); */ AuTraceErrPtr(dentry); return dentry; }
int unionfs_ioctl_addbranch(struct inode *inode, unsigned int cmd, unsigned long arg) { int err; struct unionfs_addbranch_args *addargs = NULL; struct nameidata nd; char *path = NULL; int gen; int i; int pobjects; struct unionfs_usi_data *new_data = NULL; struct dentry **new_udi_dentry = NULL; struct inode **new_uii_inode = NULL; struct dentry *root = NULL; struct dentry *hidden_root = NULL; print_entry_location(); err = -ENOMEM; addargs = KMALLOC(sizeof(struct unionfs_addbranch_args), GFP_KERNEL); if (!addargs) goto out; err = -EFAULT; if (copy_from_user (addargs, (const void __user *)arg, sizeof(struct unionfs_addbranch_args))) goto out; err = -EINVAL; if (addargs->ab_perms & ~(MAY_READ | MAY_WRITE | MAY_NFSRO)) goto out; if (!(addargs->ab_perms & MAY_READ)) goto out; err = -E2BIG; if (sbend(inode->i_sb) > FD_SETSIZE) goto out; err = -ENOMEM; if (!(path = getname((const char __user *)addargs->ab_path))) goto out; err = path_lookup(path, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) goto out; if ((err = check_branch(&nd))) { path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } unionfs_write_lock(inode->i_sb); lock_dentry(inode->i_sb->s_root); root = inode->i_sb->s_root; for (i = dbstart(inode->i_sb->s_root); i <= dbend(inode->i_sb->s_root); i++) { hidden_root = dtohd_index(root, i); if (is_branch_overlap(hidden_root, nd.dentry)) { err = -EINVAL; goto out; } } err = -EINVAL; if (addargs->ab_branch < 0 || (addargs->ab_branch > (sbend(inode->i_sb) + 1))) goto out; if ((err = newputmap(inode->i_sb))) goto out; stopd(inode->i_sb)->b_end++; dtopd(inode->i_sb->s_root)->udi_bcount++; set_dbend(inode->i_sb->s_root, dbend(inode->i_sb->s_root) + 1); itopd(inode->i_sb->s_root->d_inode)->b_end++; atomic_inc(&stopd(inode->i_sb)->usi_generation); gen = atomic_read(&stopd(inode->i_sb)->usi_generation); pobjects = sbend(inode->i_sb) + 1; /* Reallocate the dynamic structures. */ new_data = alloc_new_data(pobjects); new_udi_dentry = alloc_new_dentries(pobjects); new_uii_inode = KZALLOC(sizeof(struct inode *) * pobjects, GFP_KERNEL); if (!new_udi_dentry || !new_uii_inode || !new_data) { err = -ENOMEM; goto out; } /* Copy the in-place values to our new structure. */ for (i = 0; i < addargs->ab_branch; i++) { atomic_set(&(new_data[i].sbcount), branch_count(inode->i_sb, i)); new_data[i].branchperms = branchperms(inode->i_sb, i); new_data[i].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); new_data[i].sb = stohs_index(inode->i_sb, i); new_udi_dentry[i] = dtohd_index(inode->i_sb->s_root, i); new_uii_inode[i] = itohi_index(inode->i_sb->s_root->d_inode, i); } /* Shift the ends to the right (only handle reallocated bits). */ for (i = sbend(inode->i_sb) - 1; i >= (int)addargs->ab_branch; i--) { int j = i + 1; int pmindex; atomic_set(&new_data[j].sbcount, branch_count(inode->i_sb, i)); new_data[j].branchperms = branchperms(inode->i_sb, i); new_data[j].hidden_mnt = stohiddenmnt_index(inode->i_sb, i); new_data[j].sb = stohs_index(inode->i_sb, i); new_udi_dentry[j] = dtohd_index(inode->i_sb->s_root, i); new_uii_inode[j] = itohi_index(inode->i_sb->s_root->d_inode, i); /* Update the newest putmap, so it is correct for later. */ pmindex = stopd(inode->i_sb)->usi_lastputmap; pmindex -= stopd(inode->i_sb)->usi_firstputmap; stopd(inode->i_sb)->usi_putmaps[pmindex]->map[i] = j; } /* Now we can free the old ones. */ KFREE(dtopd(inode->i_sb->s_root)->udi_dentry); KFREE(itopd(inode->i_sb->s_root->d_inode)->uii_inode); KFREE(stopd(inode->i_sb)->usi_data); /* Update the real pointers. */ dtohd_ptr(inode->i_sb->s_root) = new_udi_dentry; itohi_ptr(inode->i_sb->s_root->d_inode) = new_uii_inode; stopd(inode->i_sb)->usi_data = new_data; /* Re-NULL the new ones so we don't try to free them. */ new_data = NULL; new_udi_dentry = NULL; new_uii_inode = NULL; /* Put the new dentry information into it's slot. */ set_dtohd_index(inode->i_sb->s_root, addargs->ab_branch, nd.dentry); set_itohi_index(inode->i_sb->s_root->d_inode, addargs->ab_branch, IGRAB(nd.dentry->d_inode)); set_branchperms(inode->i_sb, addargs->ab_branch, addargs->ab_perms); set_branch_count(inode->i_sb, addargs->ab_branch, 0); set_stohiddenmnt_index(inode->i_sb, addargs->ab_branch, nd.mnt); set_stohs_index(inode->i_sb, addargs->ab_branch, nd.dentry->d_sb); atomic_set(&dtopd(inode->i_sb->s_root)->udi_generation, gen); atomic_set(&itopd(inode->i_sb->s_root->d_inode)->uii_generation, gen); fixputmaps(inode->i_sb); out: unlock_dentry(inode->i_sb->s_root); unionfs_write_unlock(inode->i_sb); KFREE(new_udi_dentry); KFREE(new_uii_inode); KFREE(new_data); KFREE(addargs); if (path) putname(path); print_exit_status(err); return err; }
/* * Safely creates '/proc/systemtap' (if necessary) and * '/proc/systemtap/{module_name}'. * * NB: this function is suitable to call from early in the the * module-init function, and doesn't rely on any other facilities * in our runtime. PR19833. See also PR15408. */ static int _stp_mkdir_proc_module(void) { int found = 0; static char proc_root_name[STP_MODULE_NAME_LEN + sizeof("systemtap/")]; #if defined(STAPCONF_PATH_LOOKUP) || defined(STAPCONF_KERN_PATH_PARENT) struct nameidata nd; #else /* STAPCONF_VFS_PATH_LOOKUP or STAPCONF_KERN_PATH */ struct path path; #if defined(STAPCONF_VFS_PATH_LOOKUP) struct vfsmount *mnt; #endif int rc; #endif /* STAPCONF_VFS_PATH_LOOKUP or STAPCONF_KERN_PATH */ if (_stp_proc_root != NULL) return 0; #if defined(STAPCONF_PATH_LOOKUP) || defined(STAPCONF_KERN_PATH_PARENT) /* Why "/proc/systemtap/foo"? kern_path_parent() is basically * the same thing as calling the old path_lookup() with flags * set to LOOKUP_PARENT, which means to look up the parent of * the path, which in this case is "/proc/systemtap". */ if (! kern_path_parent("/proc/systemtap/foo", &nd)) { found = 1; #ifdef STAPCONF_NAMEIDATA_CLEANUP path_put(&nd.path); #else /* !STAPCONF_NAMEIDATA_CLEANUP */ path_release(&nd); #endif /* !STAPCONF_NAMEIDATA_CLEANUP */ } #elif defined(STAPCONF_KERN_PATH) /* Prefer kern_path() over vfs_path_lookup(), since on some * kernels the declaration for vfs_path_lookup() was moved to * a private header. */ /* See if '/proc/systemtap' exists. */ rc = kern_path("/proc/systemtap", 0, &path); if (rc == 0) { found = 1; path_put (&path); } #else /* STAPCONF_VFS_PATH_LOOKUP */ /* See if '/proc/systemtap' exists. */ if (! init_pid_ns.proc_mnt) { errk("Unable to create '/proc/systemap':" " '/proc' doesn't exist.\n"); goto done; } mnt = init_pid_ns.proc_mnt; rc = vfs_path_lookup(mnt->mnt_root, mnt, "systemtap", 0, &path); if (rc == 0) { found = 1; path_put (&path); } #endif /* STAPCONF_VFS_PATH_LOOKUP */ /* If we couldn't find "/proc/systemtap", create it. */ if (!found) { struct proc_dir_entry *de; de = proc_mkdir ("systemtap", NULL); if (de == NULL) { errk("Unable to create '/proc/systemap':" " proc_mkdir failed.\n"); goto done; } } /* Create the "systemtap/{module_name} directory in procfs. */ strlcpy(proc_root_name, "systemtap/", sizeof(proc_root_name)); strlcat(proc_root_name, THIS_MODULE->name, sizeof(proc_root_name)); _stp_proc_root = proc_mkdir(proc_root_name, NULL); #ifdef STAPCONF_PROCFS_OWNER if (_stp_proc_root != NULL) _stp_proc_root->owner = THIS_MODULE; #endif if (_stp_proc_root == NULL) errk("Unable to create '/proc/systemap/%s':" " proc_mkdir failed.\n", THIS_MODULE->name); done: return (_stp_proc_root) ? 0 : -EINVAL; }
static int parse_dirs_option(struct super_block *sb, struct unionfs_dentry_info *hidden_root_info, char *options) { struct nameidata nd; char *name; int err = 0; int branches = 1; int bindex = 0; int i = 0; int j = 0; struct dentry *dent1 = NULL; struct dentry *dent2 = NULL; if (options[0] == '\0') { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } /* Each colon means we have a separator, this is really just a rough * guess, since strsep will handle empty fields for us. */ for (i = 0; options[i]; i++) { if (options[i] == ':') branches++; } /* allocate space for underlying pointers to hidden dentry */ if (!(stopd(sb)->usi_data = alloc_new_data(branches))) { err = -ENOMEM; goto out; } if (!(hidden_root_info->udi_dentry = alloc_new_dentries(branches))) { err = -ENOMEM; goto out; } /* now parsing the string b1:b2=rw:b3=ro:b4 */ branches = 0; while ((name = strsep(&options, ":")) != NULL) { int perms; if (!*name) continue; branches++; /* strip off =rw or =ro if it is specified. */ perms = parse_branch_mode(name); if (!bindex && !(perms & MAY_WRITE)) { err = -EINVAL; goto out; } fist_dprint(4, "unionfs: using directory: %s (%c%c%c)\n", name, perms & MAY_READ ? 'r' : '-', perms & MAY_WRITE ? 'w' : '-', perms & MAY_NFSRO ? 'n' : '-'); err = path_lookup(name, LOOKUP_FOLLOW, &nd); RECORD_PATH_LOOKUP(&nd); if (err) { printk(KERN_WARNING "unionfs: error accessing " "hidden directory '%s' (error %d)\n", name, err); goto out; } if ((err = check_branch(&nd))) { printk(KERN_WARNING "unionfs: hidden directory " "'%s' is not a valid branch\n", name); path_release(&nd); RECORD_PATH_RELEASE(&nd); goto out; } hidden_root_info->udi_dentry[bindex] = nd.dentry; set_stohiddenmnt_index(sb, bindex, nd.mnt); set_branchperms(sb, bindex, perms); set_branch_count(sb, bindex, 0); if (hidden_root_info->udi_bstart < 0) hidden_root_info->udi_bstart = bindex; hidden_root_info->udi_bend = bindex; bindex++; } if (branches == 0) { printk(KERN_WARNING "unionfs: no branches specified\n"); err = -EINVAL; goto out; } BUG_ON(branches != (hidden_root_info->udi_bend + 1)); /* ensure that no overlaps exist in the branches */ for (i = 0; i < branches; i++) { for (j = i + 1; j < branches; j++) { dent1 = hidden_root_info->udi_dentry[i]; dent2 = hidden_root_info->udi_dentry[j]; if (is_branch_overlap(dent1, dent2)) { goto out_overlap; } } } out_overlap: if (i != branches) { printk(KERN_WARNING "unionfs: branches %d and %d overlap\n", i, j); err = -EINVAL; goto out; } out: if (err) { for (i = 0; i < branches; i++) { if (hidden_root_info->udi_dentry[i]) DPUT(hidden_root_info->udi_dentry[i]); } KFREE(hidden_root_info->udi_dentry); KFREE(stopd(sb)->usi_data); /* MUST clear the pointers to prevent potential double free if * the caller dies later on */ hidden_root_info->udi_dentry = NULL; stopd(sb)->usi_data = NULL; } return err; }
static long do_sys_truncate(const char __user * path, loff_t length) { struct nameidata nd; struct inode * inode; int error; error = -EINVAL; if (length < 0) /* sorry, but loff_t says... */ goto out; error = user_path_walk(path, &nd); if (error) goto out; inode = nd.dentry->d_inode; /* For directories it's -EISDIR, for other non-regulars - -EINVAL */ error = -EISDIR; if (S_ISDIR(inode->i_mode)) goto dput_and_out; error = -EINVAL; if (!S_ISREG(inode->i_mode)) goto dput_and_out; error = vfs_permission(&nd, MAY_WRITE); if (error) goto dput_and_out; error = -EROFS; if (IS_RDONLY(inode)) goto dput_and_out; error = -EPERM; if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) goto dput_and_out; error = get_write_access(inode); if (error) goto dput_and_out; /* * Make sure that there are no leases. get_write_access() protects * against the truncate racing with a lease-granting setlease(). */ error = break_lease(inode, FMODE_WRITE); if (error) goto put_write_and_out; error = locks_verify_truncate(inode, NULL, length); if (!error) { DQUOT_INIT(inode); error = do_truncate(nd.dentry, nd.mnt, length, 0, NULL); } put_write_and_out: put_write_access(inode); dput_and_out: path_release(&nd); out: return error; }