diff options
Diffstat (limited to 'fs/cifs/cifsfs.c')
-rw-r--r-- | fs/cifs/cifsfs.c | 233 |
1 files changed, 185 insertions, 48 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index 493b74ca5648..989442dcfb45 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -104,46 +104,25 @@ cifs_sb_deactive(struct super_block *sb) } static int -cifs_read_super(struct super_block *sb, void *data, +cifs_read_super(struct super_block *sb, struct smb_vol *volume_info, const char *devname, int silent) { struct inode *inode; struct cifs_sb_info *cifs_sb; int rc = 0; - /* BB should we make this contingent on mount parm? */ - sb->s_flags |= MS_NODIRATIME | MS_NOATIME; - sb->s_fs_info = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); cifs_sb = CIFS_SB(sb); - if (cifs_sb == NULL) - return -ENOMEM; spin_lock_init(&cifs_sb->tlink_tree_lock); cifs_sb->tlink_tree = RB_ROOT; rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); - if (rc) { - kfree(cifs_sb); + if (rc) return rc; - } - cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages; - /* - * Copy mount params to sb for use in submounts. Better to do - * the copy here and deal with the error before cleanup gets - * complicated post-mount. - */ - if (data) { - cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL); - if (cifs_sb->mountdata == NULL) { - bdi_destroy(&cifs_sb->bdi); - kfree(sb->s_fs_info); - sb->s_fs_info = NULL; - return -ENOMEM; - } - } + cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages; - rc = cifs_mount(sb, cifs_sb, devname); + rc = cifs_mount(sb, cifs_sb, volume_info, devname); if (rc) { if (!silent) @@ -194,15 +173,7 @@ out_no_root: cifs_umount(sb, cifs_sb); out_mount_failed: - if (cifs_sb) { - if (cifs_sb->mountdata) { - kfree(cifs_sb->mountdata); - cifs_sb->mountdata = NULL; - } - unload_nls(cifs_sb->local_nls); - bdi_destroy(&cifs_sb->bdi); - kfree(cifs_sb); - } + bdi_destroy(&cifs_sb->bdi); return rc; } @@ -237,7 +208,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) { struct super_block *sb = dentry->d_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); int rc = -EOPNOTSUPP; int xid; @@ -390,7 +361,7 @@ static int cifs_show_options(struct seq_file *s, struct vfsmount *m) { struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); - struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb); + struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct sockaddr *srcaddr; srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr; @@ -444,14 +415,20 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) seq_printf(s, ",nocase"); if (tcon->retry) seq_printf(s, ",hard"); - if (cifs_sb->prepath) - seq_printf(s, ",prepath=%s", cifs_sb->prepath); + if (tcon->unix_ext) + seq_printf(s, ",unix"); + else + seq_printf(s, ",nounix"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) seq_printf(s, ",posixpaths"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) seq_printf(s, ",setuids"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) seq_printf(s, ",serverino"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) + seq_printf(s, ",rwpidforward"); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) + seq_printf(s, ",forcemand"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) seq_printf(s, ",directio"); if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) @@ -484,7 +461,7 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) static void cifs_umount_begin(struct super_block *sb) { struct cifs_sb_info *cifs_sb = CIFS_SB(sb); - struct cifsTconInfo *tcon; + struct cifs_tcon *tcon; if (cifs_sb == NULL) return; @@ -559,29 +536,189 @@ static const struct super_operations cifs_super_ops = { #endif }; +/* + * Get root dentry from superblock according to prefix path mount option. + * Return dentry with refcount + 1 on success and NULL otherwise. + */ +static struct dentry * +cifs_get_root(struct smb_vol *vol, struct super_block *sb) +{ + int xid, rc; + struct inode *inode; + struct qstr name; + struct dentry *dparent = NULL, *dchild = NULL, *alias; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + unsigned int i, full_len, len; + char *full_path = NULL, *pstart; + char sep; + + full_path = cifs_build_path_to_root(vol, cifs_sb, + cifs_sb_master_tcon(cifs_sb)); + if (full_path == NULL) + return NULL; + + cFYI(1, "Get root dentry for %s", full_path); + + xid = GetXid(); + sep = CIFS_DIR_SEP(cifs_sb); + dparent = dget(sb->s_root); + full_len = strlen(full_path); + full_path[full_len] = sep; + pstart = full_path + 1; + + for (i = 1, len = 0; i <= full_len; i++) { + if (full_path[i] != sep || !len) { + len++; + continue; + } + + full_path[i] = 0; + cFYI(1, "get dentry for %s", pstart); + + name.name = pstart; + name.len = len; + name.hash = full_name_hash(pstart, len); + dchild = d_lookup(dparent, &name); + if (dchild == NULL) { + cFYI(1, "not exists"); + dchild = d_alloc(dparent, &name); + if (dchild == NULL) { + dput(dparent); + dparent = NULL; + goto out; + } + } + + cFYI(1, "get inode"); + if (dchild->d_inode == NULL) { + cFYI(1, "not exists"); + inode = NULL; + if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext) + rc = cifs_get_inode_info_unix(&inode, full_path, + sb, xid); + else + rc = cifs_get_inode_info(&inode, full_path, + NULL, sb, xid, NULL); + if (rc) { + dput(dchild); + dput(dparent); + dparent = NULL; + goto out; + } + alias = d_materialise_unique(dchild, inode); + if (alias != NULL) { + dput(dchild); + if (IS_ERR(alias)) { + dput(dparent); + dparent = NULL; + goto out; + } + dchild = alias; + } + } + cFYI(1, "parent %p, child %p", dparent, dchild); + + dput(dparent); + dparent = dchild; + len = 0; + pstart = full_path + i + 1; + full_path[i] = sep; + } +out: + _FreeXid(xid); + kfree(full_path); + return dparent; +} + static struct dentry * cifs_do_mount(struct file_system_type *fs_type, - int flags, const char *dev_name, void *data) + int flags, const char *dev_name, void *data) { int rc; struct super_block *sb; - - sb = sget(fs_type, NULL, set_anon_super, NULL); + struct cifs_sb_info *cifs_sb; + struct smb_vol *volume_info; + struct cifs_mnt_data mnt_data; + struct dentry *root; cFYI(1, "Devname: %s flags: %d ", dev_name, flags); - if (IS_ERR(sb)) - return ERR_CAST(sb); + rc = cifs_setup_volume_info(&volume_info, (char *)data, dev_name); + if (rc) + return ERR_PTR(rc); + + cifs_sb = kzalloc(sizeof(struct cifs_sb_info), GFP_KERNEL); + if (cifs_sb == NULL) { + root = ERR_PTR(-ENOMEM); + goto out; + } + + cifs_setup_cifs_sb(volume_info, cifs_sb); + + mnt_data.vol = volume_info; + mnt_data.cifs_sb = cifs_sb; + mnt_data.flags = flags; + + sb = sget(fs_type, cifs_match_super, set_anon_super, &mnt_data); + if (IS_ERR(sb)) { + root = ERR_CAST(sb); + goto out_cifs_sb; + } + + if (sb->s_fs_info) { + cFYI(1, "Use existing superblock"); + goto out_shared; + } + + /* + * Copy mount params for use in submounts. Better to do + * the copy here and deal with the error before cleanup gets + * complicated post-mount. + */ + cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL); + if (cifs_sb->mountdata == NULL) { + root = ERR_PTR(-ENOMEM); + goto out_super; + } sb->s_flags = flags; + /* BB should we make this contingent on mount parm? */ + sb->s_flags |= MS_NODIRATIME | MS_NOATIME; + sb->s_fs_info = cifs_sb; - rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0); + rc = cifs_read_super(sb, volume_info, dev_name, + flags & MS_SILENT ? 1 : 0); if (rc) { - deactivate_locked_super(sb); - return ERR_PTR(rc); + root = ERR_PTR(rc); + goto out_super; } + sb->s_flags |= MS_ACTIVE; - return dget(sb->s_root); + + root = cifs_get_root(volume_info, sb); + if (root == NULL) + goto out_super; + + cFYI(1, "dentry root is: %p", root); + goto out; + +out_shared: + root = cifs_get_root(volume_info, sb); + if (root) + cFYI(1, "dentry root is: %p", root); + goto out; + +out_super: + kfree(cifs_sb->mountdata); + deactivate_locked_super(sb); + +out_cifs_sb: + unload_nls(cifs_sb->local_nls); + kfree(cifs_sb); + +out: + cifs_cleanup_volume_info(&volume_info); + return root; } static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, |