Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[pandora-kernel.git] / fs / cifs / cifsfs.c
index 493b74c..989442d 100644 (file)
@@ -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,