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