diff options
Diffstat (limited to 'fs/smb/client/file.c')
| -rw-r--r-- | fs/smb/client/file.c | 143 |
1 files changed, 70 insertions, 73 deletions
diff --git a/fs/smb/client/file.c b/fs/smb/client/file.c index f3ddcdf406c8..a69e05f86d7e 100644 --- a/fs/smb/client/file.c +++ b/fs/smb/client/file.c @@ -255,7 +255,7 @@ static void cifs_begin_writeback(struct netfs_io_request *wreq) struct cifs_io_request *req = container_of(wreq, struct cifs_io_request, rreq); int ret; - ret = cifs_get_writable_file(CIFS_I(wreq->inode), FIND_WR_ANY, &req->cfile); + ret = cifs_get_writable_file(CIFS_I(wreq->inode), FIND_ANY, &req->cfile); if (ret) { cifs_dbg(VFS, "No writable handle in writepages ret=%d\n", ret); return; @@ -584,15 +584,8 @@ static int cifs_nt_open(const char *full_path, struct inode *inode, struct cifs_ *********************************************************************/ disposition = cifs_get_disposition(f_flags); - /* BB pass O_SYNC flag through on file attributes .. BB */ - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; + create_options |= cifs_open_create_options(f_flags, create_options); retry_open: oparms = (struct cifs_open_parms) { @@ -711,8 +704,6 @@ struct cifsFileInfo *cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, mutex_init(&cfile->fh_mutex); spin_lock_init(&cfile->file_info_lock); - cifs_sb_active(inode->i_sb); - /* * If the server returned a read oplock and we have mandatory brlocks, * set oplock level to None. @@ -767,7 +758,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) struct inode *inode = d_inode(cifs_file->dentry); struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifsLockInfo *li, *tmp; - struct super_block *sb = inode->i_sb; /* * Delete any outstanding lock records. We'll lose them when the file @@ -785,7 +775,6 @@ static void cifsFileInfo_put_final(struct cifsFileInfo *cifs_file) cifs_put_tlink(cifs_file->tlink); dput(cifs_file->dentry); - cifs_sb_deactive(sb); kfree(cifs_file->symlink_target); kfree(cifs_file); } @@ -967,7 +956,7 @@ int cifs_file_flush(const unsigned int xid, struct inode *inode, return tcon->ses->server->ops->flush(xid, tcon, &cfile->fid); } - rc = cifs_get_writable_file(CIFS_I(inode), FIND_WR_ANY, &cfile); + rc = cifs_get_writable_file(CIFS_I(inode), FIND_ANY, &cfile); if (!rc) { tcon = tlink_tcon(cfile->tlink); rc = tcon->ses->server->ops->flush(xid, tcon, &cfile->fid); @@ -992,7 +981,7 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry) return -ERESTARTSYS; mapping_set_error(inode->i_mapping, rc); - cfile = find_writable_file(cinode, FIND_WR_FSUID_ONLY); + cfile = find_writable_file(cinode, FIND_FSUID_ONLY); rc = cifs_file_flush(xid, inode, cfile); if (!rc) { if (cfile) { @@ -1004,7 +993,6 @@ static int cifs_do_truncate(const unsigned int xid, struct dentry *dentry) if (!rc) { netfs_resize_file(&cinode->netfs, 0, true); cifs_setsize(inode, 0); - inode->i_blocks = 0; } } if (cfile) @@ -1072,32 +1060,29 @@ int cifs_open(struct inode *inode, struct file *file) /* Get the cached handle as SMB2 close is deferred */ if (OPEN_FMODE(file->f_flags) & FMODE_WRITE) { - rc = cifs_get_writable_path(tcon, full_path, - FIND_WR_FSUID_ONLY | - FIND_WR_NO_PENDING_DELETE, - &cfile); + rc = __cifs_get_writable_file(CIFS_I(inode), + FIND_FSUID_ONLY | + FIND_NO_PENDING_DELETE | + FIND_OPEN_FLAGS, + file->f_flags, &cfile); } else { - rc = cifs_get_readable_path(tcon, full_path, &cfile); + cfile = __find_readable_file(CIFS_I(inode), + FIND_NO_PENDING_DELETE | + FIND_OPEN_FLAGS, + file->f_flags); + rc = cfile ? 0 : -ENOENT; } if (rc == 0) { - unsigned int oflags = file->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC); - unsigned int cflags = cfile->f_flags & ~(O_CREAT|O_EXCL|O_TRUNC); - - if (cifs_convert_flags(oflags, 0) == cifs_convert_flags(cflags, 0) && - (oflags & (O_SYNC|O_DIRECT)) == (cflags & (O_SYNC|O_DIRECT))) { - file->private_data = cfile; - spin_lock(&CIFS_I(inode)->deferred_lock); - cifs_del_deferred_close(cfile); - spin_unlock(&CIFS_I(inode)->deferred_lock); - goto use_cache; - } - _cifsFileInfo_put(cfile, true, false); - } else { - /* hard link on the defeered close file */ - rc = cifs_get_hardlink_path(tcon, inode, file); - if (rc) - cifs_close_deferred_file(CIFS_I(inode)); - } + file->private_data = cfile; + spin_lock(&CIFS_I(inode)->deferred_lock); + cifs_del_deferred_close(cfile); + spin_unlock(&CIFS_I(inode)->deferred_lock); + goto use_cache; + } + /* hard link on the deferred close file */ + rc = cifs_get_hardlink_path(tcon, inode, file); + if (rc) + cifs_close_deferred_file(CIFS_I(inode)); if (server->oplocks) oplock = REQ_OPLOCK; @@ -1318,13 +1303,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) rdwr_for_fscache = 1; desired_access = cifs_convert_flags(cfile->f_flags, rdwr_for_fscache); - - /* O_SYNC also has bit for O_DSYNC so following check picks up either */ - if (cfile->f_flags & O_SYNC) - create_options |= CREATE_WRITE_THROUGH; - - if (cfile->f_flags & O_DIRECT) - create_options |= CREATE_NO_BUFFER; + create_options |= cifs_open_create_options(cfile->f_flags, + create_options); if (server->ops->get_lease_key) server->ops->get_lease_key(inode, &cfile->fid); @@ -2528,10 +2508,33 @@ void cifs_write_subrequest_terminated(struct cifs_io_subrequest *wdata, ssize_t netfs_write_subrequest_terminated(&wdata->subreq, result); } -struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, - bool fsuid_only) +static bool open_flags_match(struct cifsInodeInfo *cinode, + unsigned int oflags, unsigned int cflags) +{ + struct inode *inode = &cinode->netfs.inode; + int crw = 0, orw = 0; + + oflags &= ~(O_CREAT | O_EXCL | O_TRUNC); + cflags &= ~(O_CREAT | O_EXCL | O_TRUNC); + + if (cifs_fscache_enabled(inode)) { + if (OPEN_FMODE(cflags) & FMODE_WRITE) + crw = 1; + if (OPEN_FMODE(oflags) & FMODE_WRITE) + orw = 1; + } + if (cifs_convert_flags(oflags, orw) != cifs_convert_flags(cflags, crw)) + return false; + + return (oflags & (O_SYNC | O_DIRECT)) == (cflags & (O_SYNC | O_DIRECT)); +} + +struct cifsFileInfo *__find_readable_file(struct cifsInodeInfo *cifs_inode, + unsigned int find_flags, + unsigned int open_flags) { struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode); + bool fsuid_only = find_flags & FIND_FSUID_ONLY; struct cifsFileInfo *open_file = NULL; /* only filter by fsuid on multiuser mounts */ @@ -2545,6 +2548,13 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { if (fsuid_only && !uid_eq(open_file->uid, current_fsuid())) continue; + if ((find_flags & FIND_NO_PENDING_DELETE) && + open_file->status_file_deleted) + continue; + if ((find_flags & FIND_OPEN_FLAGS) && + !open_flags_match(cifs_inode, open_flags, + open_file->f_flags)) + continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) { if ((!open_file->invalidHandle)) { /* found a good file */ @@ -2563,17 +2573,17 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode, } /* Return -EBADF if no handle is found and general rc otherwise */ -int -cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, int flags, - struct cifsFileInfo **ret_file) +int __cifs_get_writable_file(struct cifsInodeInfo *cifs_inode, + unsigned int find_flags, unsigned int open_flags, + struct cifsFileInfo **ret_file) { struct cifsFileInfo *open_file, *inv_file = NULL; struct cifs_sb_info *cifs_sb; bool any_available = false; int rc = -EBADF; unsigned int refind = 0; - bool fsuid_only = flags & FIND_WR_FSUID_ONLY; - bool with_delete = flags & FIND_WR_WITH_DELETE; + bool fsuid_only = find_flags & FIND_FSUID_ONLY; + bool with_delete = find_flags & FIND_WITH_DELETE; *ret_file = NULL; /* @@ -2607,9 +2617,13 @@ refind_writable: continue; if (with_delete && !(open_file->fid.access & DELETE)) continue; - if ((flags & FIND_WR_NO_PENDING_DELETE) && + if ((find_flags & FIND_NO_PENDING_DELETE) && open_file->status_file_deleted) continue; + if ((find_flags & FIND_OPEN_FLAGS) && + !open_flags_match(cifs_inode, open_flags, + open_file->f_flags)) + continue; if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) { if (!open_file->invalidHandle) { /* found a good writable file */ @@ -2726,17 +2740,7 @@ cifs_get_readable_path(struct cifs_tcon *tcon, const char *name, cinode = CIFS_I(d_inode(cfile->dentry)); spin_unlock(&tcon->open_file_lock); free_dentry_path(page); - *ret_file = find_readable_file(cinode, 0); - if (*ret_file) { - spin_lock(&cinode->open_file_lock); - if ((*ret_file)->status_file_deleted) { - spin_unlock(&cinode->open_file_lock); - cifsFileInfo_put(*ret_file); - *ret_file = NULL; - } else { - spin_unlock(&cinode->open_file_lock); - } - } + *ret_file = find_readable_file(cinode, FIND_ANY); return *ret_file ? 0 : -ENOENT; } @@ -2808,7 +2812,7 @@ int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync) } if ((OPEN_FMODE(smbfile->f_flags) & FMODE_WRITE) == 0) { - smbfile = find_writable_file(CIFS_I(inode), FIND_WR_ANY); + smbfile = find_writable_file(CIFS_I(inode), FIND_ANY); if (smbfile) { rc = server->ops->flush(xid, tcon, &smbfile->fid); cifsFileInfo_put(smbfile); @@ -3163,12 +3167,6 @@ void cifs_oplock_break(struct work_struct *work) __u64 persistent_fid, volatile_fid; __u16 net_fid; - /* - * Hold a reference to the superblock to prevent it and its inodes from - * being freed while we are accessing cinode. Otherwise, _cifsFileInfo_put() - * may release the last reference to the sb and trigger inode eviction. - */ - cifs_sb_active(sb); wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, TASK_UNINTERRUPTIBLE); @@ -3253,7 +3251,6 @@ oplock_break_ack: cifs_put_tlink(tlink); out: cifs_done_oplock_break(cinode); - cifs_sb_deactive(sb); } static int cifs_swap_activate(struct swap_info_struct *sis, |
