|
@@ -3,7 +3,7 @@
|
|
*
|
|
*
|
|
* vfs operations that deal with dentries
|
|
* vfs operations that deal with dentries
|
|
*
|
|
*
|
|
- * Copyright (C) International Business Machines Corp., 2002,2008
|
|
|
|
|
|
+ * Copyright (C) International Business Machines Corp., 2002,2009
|
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
|
* Author(s): Steve French (sfrench@us.ibm.com)
|
|
*
|
|
*
|
|
* This library is free software; you can redistribute it and/or modify
|
|
* This library is free software; you can redistribute it and/or modify
|
|
@@ -129,6 +129,78 @@ cifs_bp_rename_retry:
|
|
return full_path;
|
|
return full_path;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int cifs_posix_open(char *full_path, struct inode **pinode,
|
|
|
|
+ struct super_block *sb, int mode, int oflags,
|
|
|
|
+ int *poplock, __u16 *pnetfid, int xid)
|
|
|
|
+{
|
|
|
|
+ int rc;
|
|
|
|
+ __u32 oplock;
|
|
|
|
+ FILE_UNIX_BASIC_INFO *presp_data;
|
|
|
|
+ __u32 posix_flags = 0;
|
|
|
|
+ struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
|
|
|
+
|
|
|
|
+ cFYI(1, ("posix open %s", full_path));
|
|
|
|
+
|
|
|
|
+ presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL);
|
|
|
|
+ if (presp_data == NULL)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+/* So far cifs posix extensions can only map the following flags.
|
|
|
|
+ There are other valid fmode oflags such as FMODE_LSEEK, FMODE_PREAD, but
|
|
|
|
+ so far we do not seem to need them, and we can treat them as local only */
|
|
|
|
+ if ((oflags & (FMODE_READ | FMODE_WRITE)) ==
|
|
|
|
+ (FMODE_READ | FMODE_WRITE))
|
|
|
|
+ posix_flags = SMB_O_RDWR;
|
|
|
|
+ else if (oflags & FMODE_READ)
|
|
|
|
+ posix_flags = SMB_O_RDONLY;
|
|
|
|
+ else if (oflags & FMODE_WRITE)
|
|
|
|
+ posix_flags = SMB_O_WRONLY;
|
|
|
|
+ if (oflags & O_CREAT)
|
|
|
|
+ posix_flags |= SMB_O_CREAT;
|
|
|
|
+ if (oflags & O_EXCL)
|
|
|
|
+ posix_flags |= SMB_O_EXCL;
|
|
|
|
+ if (oflags & O_TRUNC)
|
|
|
|
+ posix_flags |= SMB_O_TRUNC;
|
|
|
|
+ if (oflags & O_APPEND)
|
|
|
|
+ posix_flags |= SMB_O_APPEND;
|
|
|
|
+ if (oflags & O_SYNC)
|
|
|
|
+ posix_flags |= SMB_O_SYNC;
|
|
|
|
+ if (oflags & O_DIRECTORY)
|
|
|
|
+ posix_flags |= SMB_O_DIRECTORY;
|
|
|
|
+ if (oflags & O_NOFOLLOW)
|
|
|
|
+ posix_flags |= SMB_O_NOFOLLOW;
|
|
|
|
+ if (oflags & O_DIRECT)
|
|
|
|
+ posix_flags |= SMB_O_DIRECT;
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
|
|
|
|
+ pnetfid, presp_data, &oplock, full_path,
|
|
|
|
+ cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
|
|
|
+ CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto posix_open_ret;
|
|
|
|
+
|
|
|
|
+ if (presp_data->Type == cpu_to_le32(-1))
|
|
|
|
+ goto posix_open_ret; /* open ok, caller does qpathinfo */
|
|
|
|
+
|
|
|
|
+ /* get new inode and set it up */
|
|
|
|
+ if (!pinode)
|
|
|
|
+ goto posix_open_ret; /* caller does not need info */
|
|
|
|
+
|
|
|
|
+ *pinode = cifs_new_inode(sb, &presp_data->UniqueId);
|
|
|
|
+
|
|
|
|
+ /* We do not need to close the file if new_inode fails since
|
|
|
|
+ the caller will retry qpathinfo as long as inode is null */
|
|
|
|
+ if (*pinode == NULL)
|
|
|
|
+ goto posix_open_ret;
|
|
|
|
+
|
|
|
|
+ posix_fill_in_inode(*pinode, presp_data, 1);
|
|
|
|
+
|
|
|
|
+posix_open_ret:
|
|
|
|
+ kfree(presp_data);
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
static void setup_cifs_dentry(struct cifsTconInfo *tcon,
|
|
static void setup_cifs_dentry(struct cifsTconInfo *tcon,
|
|
struct dentry *direntry,
|
|
struct dentry *direntry,
|
|
struct inode *newinode)
|
|
struct inode *newinode)
|
|
@@ -150,7 +222,14 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|
int xid;
|
|
int xid;
|
|
int create_options = CREATE_NOT_DIR;
|
|
int create_options = CREATE_NOT_DIR;
|
|
int oplock = 0;
|
|
int oplock = 0;
|
|
- /* BB below access is too much for the mknod to request */
|
|
|
|
|
|
+ int oflags;
|
|
|
|
+ /*
|
|
|
|
+ * 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.
|
|
|
|
+ */
|
|
int desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
int desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
__u16 fileHandle;
|
|
__u16 fileHandle;
|
|
struct cifs_sb_info *cifs_sb;
|
|
struct cifs_sb_info *cifs_sb;
|
|
@@ -174,13 +253,43 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|
}
|
|
}
|
|
|
|
|
|
mode &= ~current->fs->umask;
|
|
mode &= ~current->fs->umask;
|
|
|
|
+ if (oplockEnabled)
|
|
|
|
+ oplock = REQ_OPLOCK;
|
|
|
|
|
|
- if (nd && (nd->flags & LOOKUP_OPEN)) {
|
|
|
|
- int oflags = nd->intent.open.flags;
|
|
|
|
|
|
+ if (nd && (nd->flags & LOOKUP_OPEN))
|
|
|
|
+ oflags = nd->intent.open.flags;
|
|
|
|
+ else
|
|
|
|
+ oflags = FMODE_READ;
|
|
|
|
+
|
|
|
|
+ if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
|
|
|
|
+ (CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
|
|
|
+ le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
|
|
|
+ rc = cifs_posix_open(full_path, &newinode, inode->i_sb,
|
|
|
|
+ mode, oflags, &oplock, &fileHandle, xid);
|
|
|
|
+ /* EIO could indicate that (posix open) operation is not
|
|
|
|
+ supported, despite what server claimed in capability
|
|
|
|
+ negotation. EREMOTE indicates DFS junction, which is not
|
|
|
|
+ handled in posix open */
|
|
|
|
+
|
|
|
|
+ if ((rc == 0) && (newinode == NULL))
|
|
|
|
+ goto cifs_create_get_file_info; /* query inode info */
|
|
|
|
+ else if (rc == 0) /* success, no need to query */
|
|
|
|
+ goto cifs_create_set_dentry;
|
|
|
|
+ else if ((rc != -EIO) && (rc != -EREMOTE) &&
|
|
|
|
+ (rc != -EOPNOTSUPP)) /* path not found or net err */
|
|
|
|
+ goto cifs_create_out;
|
|
|
|
+ /* else fallthrough to retry, using older open call, this is
|
|
|
|
+ case where server does not support this SMB level, and
|
|
|
|
+ falsely claims capability (also get here for DFS case
|
|
|
|
+ which should be rare for path not covered on files) */
|
|
|
|
+ }
|
|
|
|
|
|
|
|
+ if (nd && (nd->flags & LOOKUP_OPEN)) {
|
|
|
|
+ /* if the file is going to stay open, then we
|
|
|
|
+ need to set the desired access properly */
|
|
desiredAccess = 0;
|
|
desiredAccess = 0;
|
|
if (oflags & FMODE_READ)
|
|
if (oflags & FMODE_READ)
|
|
- desiredAccess |= GENERIC_READ;
|
|
|
|
|
|
+ desiredAccess |= GENERIC_READ; /* is this too little? */
|
|
if (oflags & FMODE_WRITE) {
|
|
if (oflags & FMODE_WRITE) {
|
|
desiredAccess |= GENERIC_WRITE;
|
|
desiredAccess |= GENERIC_WRITE;
|
|
if (!(oflags & FMODE_READ))
|
|
if (!(oflags & FMODE_READ))
|
|
@@ -199,8 +308,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|
|
|
|
|
/* BB add processing to set equivalent of mode - e.g. via CreateX with
|
|
/* BB add processing to set equivalent of mode - e.g. via CreateX with
|
|
ACLs */
|
|
ACLs */
|
|
- if (oplockEnabled)
|
|
|
|
- oplock = REQ_OPLOCK;
|
|
|
|
|
|
|
|
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
|
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
|
if (buf == NULL) {
|
|
if (buf == NULL) {
|
|
@@ -233,116 +340,112 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|
}
|
|
}
|
|
if (rc) {
|
|
if (rc) {
|
|
cFYI(1, ("cifs_create returned 0x%x", rc));
|
|
cFYI(1, ("cifs_create returned 0x%x", rc));
|
|
- } else {
|
|
|
|
- /* If Open reported that we actually created a file
|
|
|
|
- then we now have to set the mode if possible */
|
|
|
|
- if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
|
|
|
|
- struct cifs_unix_set_info_args args = {
|
|
|
|
|
|
+ goto cifs_create_out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* If Open reported that we actually created a file
|
|
|
|
+ then we now have to set the mode if possible */
|
|
|
|
+ if ((tcon->unix_ext) && (oplock & CIFS_CREATE_ACTION)) {
|
|
|
|
+ struct cifs_unix_set_info_args args = {
|
|
.mode = mode,
|
|
.mode = mode,
|
|
.ctime = NO_CHANGE_64,
|
|
.ctime = NO_CHANGE_64,
|
|
.atime = NO_CHANGE_64,
|
|
.atime = NO_CHANGE_64,
|
|
.mtime = NO_CHANGE_64,
|
|
.mtime = NO_CHANGE_64,
|
|
.device = 0,
|
|
.device = 0,
|
|
- };
|
|
|
|
|
|
+ };
|
|
|
|
|
|
- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
|
|
|
- args.uid = (__u64) current_fsuid();
|
|
|
|
- if (inode->i_mode & S_ISGID)
|
|
|
|
- args.gid = (__u64) inode->i_gid;
|
|
|
|
- else
|
|
|
|
- args.gid = (__u64) current_fsgid();
|
|
|
|
- } else {
|
|
|
|
- args.uid = NO_CHANGE_64;
|
|
|
|
- args.gid = NO_CHANGE_64;
|
|
|
|
- }
|
|
|
|
- CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
|
|
|
|
- cifs_sb->local_nls,
|
|
|
|
- cifs_sb->mnt_cifs_flags &
|
|
|
|
- CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
|
|
|
+ if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {
|
|
|
|
+ args.uid = (__u64) current_fsuid();
|
|
|
|
+ if (inode->i_mode & S_ISGID)
|
|
|
|
+ args.gid = (__u64) inode->i_gid;
|
|
|
|
+ else
|
|
|
|
+ args.gid = (__u64) current_fsgid();
|
|
} else {
|
|
} else {
|
|
- /* BB implement mode setting via Windows security
|
|
|
|
- descriptors e.g. */
|
|
|
|
- /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
|
|
|
|
-
|
|
|
|
- /* Could set r/o dos attribute if mode & 0222 == 0 */
|
|
|
|
|
|
+ args.uid = NO_CHANGE_64;
|
|
|
|
+ args.gid = NO_CHANGE_64;
|
|
}
|
|
}
|
|
|
|
+ CIFSSMBUnixSetInfo(xid, tcon, full_path, &args,
|
|
|
|
+ cifs_sb->local_nls,
|
|
|
|
+ cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
|
|
|
+ } else {
|
|
|
|
+ /* BB implement mode setting via Windows security
|
|
|
|
+ descriptors e.g. */
|
|
|
|
+ /* CIFSSMBWinSetPerms(xid,tcon,path,mode,-1,-1,nls);*/
|
|
|
|
|
|
- /* server might mask mode so we have to query for it */
|
|
|
|
- if (tcon->unix_ext)
|
|
|
|
- rc = cifs_get_inode_info_unix(&newinode, full_path,
|
|
|
|
- inode->i_sb, xid);
|
|
|
|
- else {
|
|
|
|
- rc = cifs_get_inode_info(&newinode, full_path,
|
|
|
|
- buf, inode->i_sb, xid,
|
|
|
|
- &fileHandle);
|
|
|
|
- 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 (inode->i_mode & S_ISGID)
|
|
|
|
- newinode->i_gid =
|
|
|
|
- inode->i_gid;
|
|
|
|
- else
|
|
|
|
- newinode->i_gid =
|
|
|
|
- current_fsgid();
|
|
|
|
- }
|
|
|
|
|
|
+ /* Could set r/o dos attribute if mode & 0222 == 0 */
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+cifs_create_get_file_info:
|
|
|
|
+ /* server might mask mode so we have to query for it */
|
|
|
|
+ if (tcon->unix_ext)
|
|
|
|
+ rc = cifs_get_inode_info_unix(&newinode, full_path,
|
|
|
|
+ inode->i_sb, xid);
|
|
|
|
+ else {
|
|
|
|
+ rc = cifs_get_inode_info(&newinode, full_path, buf,
|
|
|
|
+ inode->i_sb, xid, &fileHandle);
|
|
|
|
+ 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 (inode->i_mode & S_ISGID)
|
|
|
|
+ newinode->i_gid = inode->i_gid;
|
|
|
|
+ else
|
|
|
|
+ newinode->i_gid = current_fsgid();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ }
|
|
|
|
|
|
- if (rc != 0) {
|
|
|
|
- cFYI(1, ("Create worked, get_inode_info failed rc = %d",
|
|
|
|
- rc));
|
|
|
|
- } else
|
|
|
|
- setup_cifs_dentry(tcon, direntry, newinode);
|
|
|
|
-
|
|
|
|
- if ((nd == NULL /* nfsd case - nfs srv does not set nd */) ||
|
|
|
|
- (!(nd->flags & LOOKUP_OPEN))) {
|
|
|
|
- /* mknod case - do not leave file open */
|
|
|
|
- CIFSSMBClose(xid, tcon, fileHandle);
|
|
|
|
- } else if (newinode) {
|
|
|
|
- struct cifsFileInfo *pCifsFile =
|
|
|
|
- kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
|
|
|
-
|
|
|
|
- if (pCifsFile == NULL)
|
|
|
|
- goto cifs_create_out;
|
|
|
|
- pCifsFile->netfid = fileHandle;
|
|
|
|
- pCifsFile->pid = current->tgid;
|
|
|
|
- pCifsFile->pInode = newinode;
|
|
|
|
- pCifsFile->invalidHandle = false;
|
|
|
|
- pCifsFile->closePend = false;
|
|
|
|
- init_MUTEX(&pCifsFile->fh_sem);
|
|
|
|
- mutex_init(&pCifsFile->lock_mutex);
|
|
|
|
- INIT_LIST_HEAD(&pCifsFile->llist);
|
|
|
|
- atomic_set(&pCifsFile->wrtPending, 0);
|
|
|
|
-
|
|
|
|
- /* set the following in open now
|
|
|
|
|
|
+cifs_create_set_dentry:
|
|
|
|
+ if (rc == 0)
|
|
|
|
+ setup_cifs_dentry(tcon, direntry, newinode);
|
|
|
|
+ else
|
|
|
|
+ cFYI(1, ("Create worked, get_inode_info failed rc = %d", rc));
|
|
|
|
+
|
|
|
|
+ /* nfsd case - nfs srv does not set nd */
|
|
|
|
+ if ((nd == NULL) || (!(nd->flags & LOOKUP_OPEN))) {
|
|
|
|
+ /* mknod case - do not leave file open */
|
|
|
|
+ CIFSSMBClose(xid, tcon, fileHandle);
|
|
|
|
+ } else if (newinode) {
|
|
|
|
+ struct cifsFileInfo *pCifsFile =
|
|
|
|
+ kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
|
|
|
+
|
|
|
|
+ if (pCifsFile == NULL)
|
|
|
|
+ goto cifs_create_out;
|
|
|
|
+ pCifsFile->netfid = fileHandle;
|
|
|
|
+ pCifsFile->pid = current->tgid;
|
|
|
|
+ pCifsFile->pInode = newinode;
|
|
|
|
+ pCifsFile->invalidHandle = false;
|
|
|
|
+ pCifsFile->closePend = false;
|
|
|
|
+ init_MUTEX(&pCifsFile->fh_sem);
|
|
|
|
+ mutex_init(&pCifsFile->lock_mutex);
|
|
|
|
+ INIT_LIST_HEAD(&pCifsFile->llist);
|
|
|
|
+ atomic_set(&pCifsFile->wrtPending, 0);
|
|
|
|
+
|
|
|
|
+ /* set the following in open now
|
|
pCifsFile->pfile = file; */
|
|
pCifsFile->pfile = file; */
|
|
- write_lock(&GlobalSMBSeslock);
|
|
|
|
- list_add(&pCifsFile->tlist, &tcon->openFileList);
|
|
|
|
- pCifsInode = CIFS_I(newinode);
|
|
|
|
- if (pCifsInode) {
|
|
|
|
- /* if readable file instance put first in list*/
|
|
|
|
- if (write_only) {
|
|
|
|
- list_add_tail(&pCifsFile->flist,
|
|
|
|
- &pCifsInode->openFileList);
|
|
|
|
- } else {
|
|
|
|
- list_add(&pCifsFile->flist,
|
|
|
|
- &pCifsInode->openFileList);
|
|
|
|
- }
|
|
|
|
- if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
|
- pCifsInode->clientCanCacheAll = true;
|
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
|
- cFYI(1, ("Exclusive Oplock inode %p",
|
|
|
|
- newinode));
|
|
|
|
- } else if ((oplock & 0xF) == OPLOCK_READ)
|
|
|
|
- pCifsInode->clientCanCacheRead = true;
|
|
|
|
|
|
+ write_lock(&GlobalSMBSeslock);
|
|
|
|
+ list_add(&pCifsFile->tlist, &tcon->openFileList);
|
|
|
|
+ pCifsInode = CIFS_I(newinode);
|
|
|
|
+ if (pCifsInode) {
|
|
|
|
+ /* if readable file instance put first in list*/
|
|
|
|
+ if (write_only) {
|
|
|
|
+ list_add_tail(&pCifsFile->flist,
|
|
|
|
+ &pCifsInode->openFileList);
|
|
|
|
+ } else {
|
|
|
|
+ list_add(&pCifsFile->flist,
|
|
|
|
+ &pCifsInode->openFileList);
|
|
}
|
|
}
|
|
- write_unlock(&GlobalSMBSeslock);
|
|
|
|
|
|
+ if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
|
|
|
+ pCifsInode->clientCanCacheAll = true;
|
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
|
|
+ cFYI(1, ("Exclusive Oplock inode %p",
|
|
|
|
+ newinode));
|
|
|
|
+ } else if ((oplock & 0xF) == OPLOCK_READ)
|
|
|
|
+ pCifsInode->clientCanCacheRead = true;
|
|
}
|
|
}
|
|
|
|
+ write_unlock(&GlobalSMBSeslock);
|
|
}
|
|
}
|
|
cifs_create_out:
|
|
cifs_create_out:
|
|
kfree(buf);
|
|
kfree(buf);
|