summaryrefslogtreecommitdiff
path: root/fs/cifs/cifsfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/cifsfs.c')
-rw-r--r--fs/cifs/cifsfs.c327
1 files changed, 234 insertions, 93 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 493b74ca5648..35f9154615fa 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -104,52 +104,24 @@ cifs_sb_deactive(struct super_block *sb)
}
static int
-cifs_read_super(struct super_block *sb, void *data,
- const char *devname, int silent)
+cifs_read_super(struct super_block *sb)
{
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;
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)
+ sb->s_flags |= MS_POSIXACL;
- rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY);
- if (rc) {
- kfree(cifs_sb);
- 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;
- }
- }
+ if (cifs_sb_master_tcon(cifs_sb)->ses->capabilities & CAP_LARGE_FILES)
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
+ else
+ sb->s_maxbytes = MAX_NON_LFS;
- rc = cifs_mount(sb, cifs_sb, devname);
-
- if (rc) {
- if (!silent)
- cERROR(1, "cifs_mount failed w/return code = %d", rc);
- goto out_mount_failed;
- }
+ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
+ sb->s_time_gran = 100;
sb->s_magic = CIFS_MAGIC_NUMBER;
sb->s_op = &cifs_super_ops;
@@ -191,45 +163,14 @@ out_no_root:
if (inode)
iput(inode);
- 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);
- }
return rc;
}
-static void
-cifs_put_super(struct super_block *sb)
+static void cifs_kill_sb(struct super_block *sb)
{
- int rc = 0;
- struct cifs_sb_info *cifs_sb;
-
- cFYI(1, "In cifs_put_super");
- cifs_sb = CIFS_SB(sb);
- if (cifs_sb == NULL) {
- cFYI(1, "Empty cifs superblock info passed to unmount");
- return;
- }
-
- rc = cifs_umount(sb, cifs_sb);
- if (rc)
- cERROR(1, "cifs_umount failed with return code %d", rc);
- 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);
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+ kill_anon_super(sb);
+ cifs_umount(cifs_sb);
}
static int
@@ -237,7 +178,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;
@@ -286,9 +227,6 @@ static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
{
struct cifs_sb_info *cifs_sb;
- if (flags & IPERM_FLAG_RCU)
- return -ECHILD;
-
cifs_sb = CIFS_SB(inode->i_sb);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) {
@@ -381,6 +319,37 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)
}
}
+static void
+cifs_show_security(struct seq_file *s, struct TCP_Server_Info *server)
+{
+ seq_printf(s, ",sec=");
+
+ switch (server->secType) {
+ case LANMAN:
+ seq_printf(s, "lanman");
+ break;
+ case NTLMv2:
+ seq_printf(s, "ntlmv2");
+ break;
+ case NTLM:
+ seq_printf(s, "ntlm");
+ break;
+ case Kerberos:
+ seq_printf(s, "krb5");
+ break;
+ case RawNTLMSSP:
+ seq_printf(s, "ntlmssp");
+ break;
+ default:
+ /* shouldn't ever happen */
+ seq_printf(s, "unknown");
+ break;
+ }
+
+ if (server->sec_mode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ seq_printf(s, "i");
+}
+
/*
* cifs_show_options() is for displaying mount options in /proc/mounts.
* Not all settable options are displayed but most of the important
@@ -390,10 +359,12 @@ 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;
+ cifs_show_security(s, tcon->ses->server);
+
seq_printf(s, ",unc=%s", tcon->treeName);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
@@ -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;
@@ -541,7 +518,6 @@ static int cifs_drop_inode(struct inode *inode)
}
static const struct super_operations cifs_super_ops = {
- .put_super = cifs_put_super,
.statfs = cifs_statfs,
.alloc_inode = cifs_alloc_inode,
.destroy_inode = cifs_destroy_inode,
@@ -559,29 +535,194 @@ 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 ERR_PTR(-ENOMEM);
+
+ 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 = ERR_PTR(-ENOMEM);
+ 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 = ERR_PTR(rc);
+ goto out;
+ }
+ alias = d_materialise_unique(dchild, inode);
+ if (alias != NULL) {
+ dput(dchild);
+ if (IS_ERR(alias)) {
+ dput(dparent);
+ dparent = ERR_PTR(-EINVAL); /* XXX */
+ 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 int cifs_set_super(struct super_block *sb, void *data)
+{
+ struct cifs_mnt_data *mnt_data = data;
+ sb->s_fs_info = mnt_data->cifs_sb;
+ return set_anon_super(sb, NULL);
+}
+
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_nls;
+ }
+
+ cifs_sb->mountdata = kstrndup(data, PAGE_SIZE, GFP_KERNEL);
+ if (cifs_sb->mountdata == NULL) {
+ root = ERR_PTR(-ENOMEM);
+ goto out_cifs_sb;
+ }
- sb->s_flags = flags;
+ cifs_setup_cifs_sb(volume_info, cifs_sb);
- rc = cifs_read_super(sb, data, dev_name, flags & MS_SILENT ? 1 : 0);
+ rc = cifs_mount(cifs_sb, volume_info);
if (rc) {
- deactivate_locked_super(sb);
- return ERR_PTR(rc);
+ if (!(flags & MS_SILENT))
+ cERROR(1, "cifs_mount failed w/return code = %d", rc);
+ root = ERR_PTR(rc);
+ goto out_mountdata;
+ }
+
+ mnt_data.vol = volume_info;
+ mnt_data.cifs_sb = cifs_sb;
+ mnt_data.flags = flags;
+
+ sb = sget(fs_type, cifs_match_super, cifs_set_super, &mnt_data);
+ if (IS_ERR(sb)) {
+ root = ERR_CAST(sb);
+ cifs_umount(cifs_sb);
+ goto out;
+ }
+
+ if (sb->s_root) {
+ cFYI(1, "Use existing superblock");
+ cifs_umount(cifs_sb);
+ } else {
+ sb->s_flags = flags;
+ /* BB should we make this contingent on mount parm? */
+ sb->s_flags |= MS_NODIRATIME | MS_NOATIME;
+
+ rc = cifs_read_super(sb);
+ if (rc) {
+ root = ERR_PTR(rc);
+ goto out_super;
+ }
+
+ sb->s_flags |= MS_ACTIVE;
}
- sb->s_flags |= MS_ACTIVE;
- return dget(sb->s_root);
+
+ root = cifs_get_root(volume_info, sb);
+ if (IS_ERR(root))
+ goto out_super;
+
+ cFYI(1, "dentry root is: %p", root);
+ goto out;
+
+out_super:
+ deactivate_locked_super(sb);
+out:
+ cifs_cleanup_volume_info(&volume_info);
+ return root;
+
+out_mountdata:
+ kfree(cifs_sb->mountdata);
+out_cifs_sb:
+ kfree(cifs_sb);
+out_nls:
+ unload_nls(volume_info->local_nls);
+ goto out;
}
static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
@@ -670,7 +811,7 @@ struct file_system_type cifs_fs_type = {
.owner = THIS_MODULE,
.name = "cifs",
.mount = cifs_do_mount,
- .kill_sb = kill_anon_super,
+ .kill_sb = cifs_kill_sb,
/* .fs_flags */
};
const struct inode_operations cifs_dir_inode_ops = {