From: Pavel Shilovsky <[email protected]>

Signed-off-by: Pavel Shilovsky <[email protected]>
---
 fs/cifs/smb2inode.c |  186 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 fs/cifs/smb2proto.h |    2 +
 2 files changed, 187 insertions(+), 1 deletions(-)

diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 17297e4..ff9d174 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -23,6 +23,8 @@
 #include <linux/stat.h>
 #include <linux/slab.h>
 #include <linux/pagemap.h>
+#include <linux/namei.h>
+#include <linux/file.h>
 #include <asm/div64.h>
 #include "cifsfs.h"
 #include "cifspdu.h"
@@ -36,7 +38,7 @@
 #include "smb2proto.h"
 
 const struct inode_operations smb2_dir_inode_ops = {
-       .create = cifs_create,
+       .create = smb2_create,
        .lookup = cifs_lookup,
        .getattr = cifs_getattr,
        .unlink = smb2_unlink,
@@ -568,3 +570,185 @@ unlink_out:
        cifs_put_tlink(tlink);
        return rc;
 }
+
+static int
+smb2_create_helper(struct inode *dir, struct dentry *direntry, int mode,
+                  struct nameidata *nd, __u32 *oplock, __u64 *persist_fid,
+                  __u64 *volatile_fid, int xid, struct cifs_tcon *tcon,
+                  const char *full_path, void *data)
+{
+       int rc;
+       __u32 desired_access;
+       __u32 create_disposition;
+       __u32 create_options = CREATE_NOT_DIR;
+       __u32 oflags;
+       __le16 *smb2_path;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(dir->i_sb);
+       FILE_ALL_INFO_SMB2 *smb2_data = NULL;
+
+       if (nd) {
+               /* if the file is going to stay open, then we
+                  need to set the desired access properly */
+               oflags = nd->intent.open.file->f_flags;
+
+               /* read attributes access to get attributes of inode */
+               desired_access = FILE_READ_ATTRIBUTES;
+               if (OPEN_FMODE(oflags) & FMODE_READ)
+                       /* is this too little?*/
+                       desired_access |= GENERIC_READ;
+               if (OPEN_FMODE(oflags) & FMODE_WRITE)
+                       desired_access |= GENERIC_WRITE;
+
+               if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+                       create_disposition = FILE_CREATE;
+               else if ((oflags & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC))
+                       create_disposition = FILE_OVERWRITE_IF;
+               else if ((oflags & O_CREAT) == O_CREAT)
+                       create_disposition = FILE_OPEN_IF;
+               else {
+                       create_disposition = FILE_OPEN;
+                       cFYI(1, "Create flag not set in create function");
+               }
+       } else {
+               desired_access = GENERIC_READ | GENERIC_WRITE;
+               create_disposition = FILE_OVERWRITE_IF;
+       }
+       /* BB pass O_SYNC flag through on file attributes .. BB */
+
+       smb2_path = cifs_convert_path_to_ucs(full_path, cifs_sb->local_nls);
+       if (smb2_path == NULL) {
+               rc = -ENOMEM;
+               goto out;
+       }
+
+       rc = SMB2_open(xid, tcon, smb2_path, persist_fid, volatile_fid,
+                      desired_access, create_disposition, 0, create_options);
+       if (rc)
+               goto out;
+
+       smb2_data = kzalloc(sizeof(FILE_ALL_INFO_SMB2) + MAX_NAME*2,
+                           GFP_KERNEL);
+       if (smb2_data == NULL) {
+               rc = -ENOMEM;
+               SMB2_close(xid, tcon, *persist_fid, *volatile_fid);
+               goto out;
+       }
+
+       rc = SMB2_query_info(xid, tcon, *persist_fid, *volatile_fid, smb2_data);
+       if (rc) {
+               SMB2_close(xid, tcon, *persist_fid, *volatile_fid);
+               goto out;
+       }
+
+       move_smb2_info_to_cifs(data, smb2_data);
+
+out:
+       *oplock = 0;
+       kfree(smb2_data);
+       kfree(smb2_path);
+       return rc;
+}
+
+int
+smb2_create(struct inode *dir, struct dentry *direntry, int mode,
+           struct nameidata *nd)
+{
+       int rc = -ENOENT;
+       int xid;
+       __u32 oplock = 0;
+       /*
+        * BB below access is probably too much for mknod to request
+        *    but we have to do query and setpathinfo so requesting
+        *    less could fail (unless we want to request getatr and setatr
+        *    permissions (only).  At least for POSIX we do not have to
+        *    request so much.
+        */
+       __u64 persist_fid, volatile_fid;
+       struct cifs_sb_info *cifs_sb;
+       struct tcon_link *tlink;
+       struct cifs_tcon *tcon;
+       struct inode *newinode = NULL;
+       FILE_ALL_INFO *buf;
+       char *full_path;
+
+       xid = GetXid();
+
+       cifs_sb = CIFS_SB(dir->i_sb);
+       tlink = cifs_sb_tlink(cifs_sb);
+       if (IS_ERR(tlink)) {
+               FreeXid(xid);
+               return PTR_ERR(tlink);
+       }
+       tcon = tlink_tcon(tlink);
+
+       if (oplockEnabled)
+               oplock = REQ_OPLOCK;
+
+       buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       full_path = build_path_from_dentry(direntry);
+       if (full_path == NULL) {
+               rc = -ENOMEM;
+               goto smb2_create_out;
+       }
+
+       rc = smb2_create_helper(dir, direntry, mode, nd, &oplock, &persist_fid,
+                               &volatile_fid, xid, tcon, full_path, buf);
+       if (rc) {
+               cFYI(1, "smb2_create returned 0x%x", rc);
+               goto smb2_create_out;
+       }
+
+       rc = smb2_query_inode_info(&newinode, full_path, buf, dir->i_sb, xid);
+       if (rc) {
+               cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
+               SMB2_close(xid, tcon, persist_fid, volatile_fid);
+               goto smb2_create_out;
+       }
+
+       if (newinode) {
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM)
+                       newinode->i_mode = mode;
+               if ((oplock & CIFS_CREATE_ACTION) &&
+                   (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID)) {
+                       newinode->i_uid = current_fsuid();
+                       if (dir->i_mode & S_ISGID)
+                               newinode->i_gid = dir->i_gid;
+                       else
+                               newinode->i_gid = current_fsgid();
+               }
+       }
+
+       d_instantiate(direntry, newinode);
+
+       if (newinode && nd) {
+               struct cifsFileInfo *file_info;
+               struct file *filp;
+
+               filp = lookup_instantiate_filp(nd, direntry, generic_file_open);
+               if (IS_ERR(filp)) {
+                       rc = PTR_ERR(filp);
+                       SMB2_close(xid, tcon, persist_fid, volatile_fid);
+                       goto smb2_create_out;
+               }
+
+               file_info = smb2_new_fileinfo(persist_fid, volatile_fid,
+                                             filp, tlink, oplock);
+               if (file_info == NULL) {
+                       fput(filp);
+                       SMB2_close(xid, tcon, persist_fid, volatile_fid);
+                       rc = -ENOMEM;
+               }
+       } else {
+               SMB2_close(xid, tcon, persist_fid, volatile_fid);
+       }
+
+smb2_create_out:
+       kfree(buf);
+       kfree(full_path);
+       cifs_put_tlink(tlink);
+       FreeXid(xid);
+       return rc;
+}
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index 73bc050..5782a10 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -149,6 +149,8 @@ extern int smb2_writepages(struct address_space *mapping,
 extern int smb2_readpages(struct file *file, struct address_space *mapping,
                          struct list_head *page_list, unsigned num_pages);
 
+extern int smb2_create(struct inode *dir, struct dentry *direntry, int mode,
+                      struct nameidata *nd);
 extern int smb2_mkdir(struct inode *inode, struct dentry *direntry, int mode);
 extern int smb2_rmdir(struct inode *inode, struct dentry *direntry);
 extern int smb2_unlink(struct inode *dir, struct dentry *dentry);
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to