diff options
Diffstat (limited to 'fs/cifs/file.c')
-rw-r--r-- | fs/cifs/file.c | 113 |
1 files changed, 88 insertions, 25 deletions
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index c10f085..b1ca6a4 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -60,34 +60,32 @@ static inline int cifs_convert_flags(unsigned int flags) FILE_READ_DATA); } -static inline fmode_t cifs_posix_convert_flags(unsigned int flags) +static u32 cifs_posix_convert_flags(unsigned int flags) { - fmode_t posix_flags = 0; + u32 posix_flags = 0; if ((flags & O_ACCMODE) == O_RDONLY) - posix_flags = FMODE_READ; + posix_flags = SMB_O_RDONLY; else if ((flags & O_ACCMODE) == O_WRONLY) - posix_flags = FMODE_WRITE; - else if ((flags & O_ACCMODE) == O_RDWR) { - /* GENERIC_ALL is too much permission to request - can cause unnecessary access denied on create */ - /* return GENERIC_ALL; */ - posix_flags = FMODE_READ | FMODE_WRITE; - } - /* can not map O_CREAT or O_EXCL or O_TRUNC flags when - reopening a file. They had their effect on the original open */ - if (flags & O_APPEND) - posix_flags |= (fmode_t)O_APPEND; + posix_flags = SMB_O_WRONLY; + else if ((flags & O_ACCMODE) == O_RDWR) + posix_flags = SMB_O_RDWR; + + if (flags & O_CREAT) + posix_flags |= SMB_O_CREAT; + if (flags & O_EXCL) + posix_flags |= SMB_O_EXCL; + if (flags & O_TRUNC) + posix_flags |= SMB_O_TRUNC; + /* be safe and imply O_SYNC for O_DSYNC */ if (flags & O_DSYNC) - posix_flags |= (fmode_t)O_DSYNC; - if (flags & __O_SYNC) - posix_flags |= (fmode_t)__O_SYNC; + posix_flags |= SMB_O_SYNC; if (flags & O_DIRECTORY) - posix_flags |= (fmode_t)O_DIRECTORY; + posix_flags |= SMB_O_DIRECTORY; if (flags & O_NOFOLLOW) - posix_flags |= (fmode_t)O_NOFOLLOW; + posix_flags |= SMB_O_NOFOLLOW; if (flags & O_DIRECT) - posix_flags |= (fmode_t)O_DIRECT; + posix_flags |= SMB_O_DIRECT; return posix_flags; } @@ -159,6 +157,68 @@ client_can_cache: return rc; } +int cifs_posix_open(char *full_path, struct inode **pinode, + struct super_block *sb, int mode, unsigned int f_flags, + __u32 *poplock, __u16 *pnetfid, int xid) +{ + int rc; + FILE_UNIX_BASIC_INFO *presp_data; + __u32 posix_flags = 0; + struct cifs_sb_info *cifs_sb = CIFS_SB(sb); + struct cifs_fattr fattr; + struct tcon_link *tlink; + struct cifsTconInfo *tcon; + + cFYI(1, "posix open %s", full_path); + + presp_data = kzalloc(sizeof(FILE_UNIX_BASIC_INFO), GFP_KERNEL); + if (presp_data == NULL) + return -ENOMEM; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) { + rc = PTR_ERR(tlink); + goto posix_open_ret; + } + + tcon = tlink_tcon(tlink); + mode &= ~current_umask(); + + posix_flags = cifs_posix_convert_flags(f_flags); + rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data, + poplock, full_path, cifs_sb->local_nls, + cifs_sb->mnt_cifs_flags & + CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_put_tlink(tlink); + + if (rc) + goto posix_open_ret; + + if (presp_data->Type == cpu_to_le32(-1)) + goto posix_open_ret; /* open ok, caller does qpathinfo */ + + if (!pinode) + goto posix_open_ret; /* caller does not need info */ + + cifs_unix_basic_to_fattr(&fattr, presp_data, cifs_sb); + + /* get new inode and set it up */ + if (*pinode == NULL) { + cifs_fill_uniqueid(sb, &fattr); + *pinode = cifs_iget(sb, &fattr); + if (!*pinode) { + rc = -ENOMEM; + goto posix_open_ret; + } + } else { + cifs_fattr_to_inode(*pinode, &fattr); + } + +posix_open_ret: + kfree(presp_data); + return rc; +} + int cifs_open(struct inode *inode, struct file *file) { int rc = -EACCES; @@ -205,12 +265,10 @@ int cifs_open(struct inode *inode, struct file *file) (tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { - int oflags = (int) cifs_posix_convert_flags(file->f_flags); - oflags |= SMB_O_CREAT; /* can not refresh inode info since size could be stale */ rc = cifs_posix_open(full_path, &inode, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, - oflags, &oplock, &netfid, xid); + file->f_flags, &oplock, &netfid, xid); if (rc == 0) { cFYI(1, "posix open succeeded"); @@ -426,8 +484,13 @@ reopen_error_exit: if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && (CIFS_UNIX_POSIX_PATH_OPS_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability))) { - int oflags = (int) cifs_posix_convert_flags(file->f_flags); - /* can not refresh inode info since size could be stale */ + + /* + * O_CREAT, O_EXCL and O_TRUNC already had their effect on the + * original open. Must mask them off for a reopen. + */ + unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC); + rc = cifs_posix_open(full_path, NULL, inode->i_sb, cifs_sb->mnt_file_mode /* ignored */, oflags, &oplock, &netfid, xid); |