summaryrefslogtreecommitdiffstats
path: root/fs/cifs/cifsfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs/cifsfs.c')
-rw-r--r--fs/cifs/cifsfs.c338
1 files changed, 248 insertions, 90 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 5c412b33cd7..989442dcfb4 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -104,53 +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;
-#ifdef CONFIG_CIFS_DFS_UPCALL
- /* copy mount params to sb for use in submounts */
- /* BB: should we move this after the mount so we
- * do not have to do the copy on failed mounts?
- * BB: May be it is better to do simple copy before
- * complex operation (mount), and in case of fail
- * just exit instead of doing mount and attempting
- * undo it if this copy fails?*/
- if (data) {
- int len = strlen(data);
- cifs_sb->mountdata = kzalloc(len + 1, GFP_KERNEL);
- if (cifs_sb->mountdata == NULL) {
- bdi_destroy(&cifs_sb->bdi);
- kfree(sb->s_fs_info);
- sb->s_fs_info = NULL;
- return -ENOMEM;
- }
- strncpy(cifs_sb->mountdata, data, len + 1);
- cifs_sb->mountdata[len] = '\0';
- }
-#endif
+ cifs_sb->bdi.ra_pages = default_backing_dev_info.ra_pages;
- rc = cifs_mount(sb, cifs_sb, data, devname);
+ rc = cifs_mount(sb, cifs_sb, volume_info, devname);
if (rc) {
if (!silent)
@@ -163,7 +135,7 @@ cifs_read_super(struct super_block *sb, void *data,
sb->s_bdi = &cifs_sb->bdi;
sb->s_blocksize = CIFS_MAX_MSGSIZE;
sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
- inode = cifs_root_iget(sb, ROOT_I);
+ inode = cifs_root_iget(sb);
if (IS_ERR(inode)) {
rc = PTR_ERR(inode);
@@ -184,12 +156,12 @@ cifs_read_super(struct super_block *sb, void *data,
else
sb->s_d_op = &cifs_dentry_ops;
-#ifdef CONFIG_CIFS_EXPERIMENTAL
+#ifdef CIFS_NFSD_EXPORT
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
cFYI(1, "export ops supported");
sb->s_export_op = &cifs_export_ops;
}
-#endif /* EXPERIMENTAL */
+#endif /* CIFS_NFSD_EXPORT */
return 0;
@@ -201,17 +173,7 @@ out_no_root:
cifs_umount(sb, cifs_sb);
out_mount_failed:
- if (cifs_sb) {
-#ifdef CONFIG_CIFS_DFS_UPCALL
- if (cifs_sb->mountdata) {
- kfree(cifs_sb->mountdata);
- cifs_sb->mountdata = NULL;
- }
-#endif
- unload_nls(cifs_sb->local_nls);
- bdi_destroy(&cifs_sb->bdi);
- kfree(cifs_sb);
- }
+ bdi_destroy(&cifs_sb->bdi);
return rc;
}
@@ -231,12 +193,10 @@ cifs_put_super(struct super_block *sb)
rc = cifs_umount(sb, cifs_sb);
if (rc)
cERROR(1, "cifs_umount failed with return code %d", rc);
-#ifdef CONFIG_CIFS_DFS_UPCALL
if (cifs_sb->mountdata) {
kfree(cifs_sb->mountdata);
cifs_sb->mountdata = NULL;
}
-#endif
unload_nls(cifs_sb->local_nls);
bdi_destroy(&cifs_sb->bdi);
@@ -248,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;
@@ -401,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;
@@ -455,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)
@@ -495,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;
@@ -570,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,
@@ -618,16 +744,31 @@ static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
{
/* origin == SEEK_END => we must revalidate the cached file length */
if (origin == SEEK_END) {
- int retval;
-
- /* some applications poll for the file length in this strange
- way so we must seek to end on non-oplocked files by
- setting the revalidate time to zero */
- CIFS_I(file->f_path.dentry->d_inode)->time = 0;
-
- retval = cifs_revalidate_file(file);
- if (retval < 0)
- return (loff_t)retval;
+ int rc;
+ struct inode *inode = file->f_path.dentry->d_inode;
+
+ /*
+ * We need to be sure that all dirty pages are written and the
+ * server has the newest file length.
+ */
+ if (!CIFS_I(inode)->clientCanCacheRead && inode->i_mapping &&
+ inode->i_mapping->nrpages != 0) {
+ rc = filemap_fdatawait(inode->i_mapping);
+ if (rc) {
+ mapping_set_error(inode->i_mapping, rc);
+ return rc;
+ }
+ }
+ /*
+ * Some applications poll for the file length in this strange
+ * way so we must seek to end on non-oplocked files by
+ * setting the revalidate time to zero.
+ */
+ CIFS_I(inode)->time = 0;
+
+ rc = cifs_revalidate_file_attr(file);
+ if (rc < 0)
+ return (loff_t)rc;
}
return generic_file_llseek_unlocked(file, offset, origin);
}
@@ -760,10 +901,11 @@ const struct file_operations cifs_file_strict_ops = {
};
const struct file_operations cifs_file_direct_ops = {
- /* no aio, no readv -
- BB reevaluate whether they can be done with directio, no cache */
- .read = cifs_user_read,
- .write = cifs_user_write,
+ /* BB reevaluate whether they can be done with directio, no cache */
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = cifs_user_readv,
+ .aio_write = cifs_user_writev,
.open = cifs_open,
.release = cifs_close,
.lock = cifs_lock,
@@ -815,10 +957,11 @@ const struct file_operations cifs_file_strict_nobrl_ops = {
};
const struct file_operations cifs_file_direct_nobrl_ops = {
- /* no mmap, no aio, no readv -
- BB reevaluate whether they can be done with directio, no cache */
- .read = cifs_user_read,
- .write = cifs_user_write,
+ /* BB reevaluate whether they can be done with directio, no cache */
+ .read = do_sync_read,
+ .write = do_sync_write,
+ .aio_read = cifs_user_readv,
+ .aio_write = cifs_user_writev,
.open = cifs_open,
.release = cifs_close,
.fsync = cifs_fsync,
@@ -981,10 +1124,10 @@ init_cifs(void)
int rc = 0;
cifs_proc_init();
INIT_LIST_HEAD(&cifs_tcp_ses_list);
-#ifdef CONFIG_CIFS_EXPERIMENTAL
+#ifdef CONFIG_CIFS_DNOTIFY_EXPERIMENTAL /* unused temporarily */
INIT_LIST_HEAD(&GlobalDnotifyReqList);
INIT_LIST_HEAD(&GlobalDnotifyRsp_Q);
-#endif
+#endif /* was needed for dnotify, and will be needed for inotify when VFS fix */
/*
* Initialize Global counters
*/
@@ -1033,22 +1176,33 @@ init_cifs(void)
if (rc)
goto out_destroy_mids;
- rc = register_filesystem(&cifs_fs_type);
- if (rc)
- goto out_destroy_request_bufs;
#ifdef CONFIG_CIFS_UPCALL
rc = register_key_type(&cifs_spnego_key_type);
if (rc)
- goto out_unregister_filesystem;
-#endif
+ goto out_destroy_request_bufs;
+#endif /* CONFIG_CIFS_UPCALL */
+
+#ifdef CONFIG_CIFS_ACL
+ rc = init_cifs_idmap();
+ if (rc)
+ goto out_register_key_type;
+#endif /* CONFIG_CIFS_ACL */
+
+ rc = register_filesystem(&cifs_fs_type);
+ if (rc)
+ goto out_init_cifs_idmap;
return 0;
-#ifdef CONFIG_CIFS_UPCALL
-out_unregister_filesystem:
- unregister_filesystem(&cifs_fs_type);
+out_init_cifs_idmap:
+#ifdef CONFIG_CIFS_ACL
+ exit_cifs_idmap();
+out_register_key_type:
#endif
+#ifdef CONFIG_CIFS_UPCALL
+ unregister_key_type(&cifs_spnego_key_type);
out_destroy_request_bufs:
+#endif
cifs_destroy_request_bufs();
out_destroy_mids:
cifs_destroy_mids();
@@ -1070,6 +1224,10 @@ exit_cifs(void)
#ifdef CONFIG_CIFS_DFS_UPCALL
cifs_dfs_release_automount_timer();
#endif
+#ifdef CONFIG_CIFS_ACL
+ cifs_destroy_idmaptrees();
+ exit_cifs_idmap();
+#endif
#ifdef CONFIG_CIFS_UPCALL
unregister_key_type(&cifs_spnego_key_type);
#endif