summaryrefslogtreecommitdiff
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifsfs.c111
-rw-r--r--fs/cifs/cifsfs.h6
-rw-r--r--fs/cifs/connect.c7
-rw-r--r--fs/cifs/dir.c29
-rw-r--r--fs/cifs/file.c20
-rw-r--r--fs/cifs/readdir.c2
-rw-r--r--fs/cifs/sess.c10
7 files changed, 85 insertions, 100 deletions
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 3e2989976297..865517470967 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -35,6 +35,7 @@
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/freezer.h>
+#include <linux/namei.h>
#include <net/ipv6.h>
#include "cifsfs.h"
#include "cifspdu.h"
@@ -223,7 +224,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
return 0;
}
-static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
+static int cifs_permission(struct inode *inode, int mask)
{
struct cifs_sb_info *cifs_sb;
@@ -238,7 +239,7 @@ static int cifs_permission(struct inode *inode, int mask, unsigned int flags)
on the client (above and beyond ACL on servers) for
servers which do not support setting and viewing mode bits,
so allowing client to check permissions is useful */
- return generic_permission(inode, mask, flags, NULL);
+ return generic_permission(inode, mask);
}
static struct kmem_cache *cifs_inode_cachep;
@@ -542,14 +543,12 @@ static const struct super_operations cifs_super_ops = {
static struct dentry *
cifs_get_root(struct smb_vol *vol, struct super_block *sb)
{
- int xid, rc;
- struct inode *inode;
- struct qstr name;
- struct dentry *dparent = NULL, *dchild = NULL, *alias;
+ struct dentry *dentry;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
- unsigned int i, full_len, len;
- char *full_path = NULL, *pstart;
+ char *full_path = NULL;
+ char *s, *p;
char sep;
+ int xid;
full_path = cifs_build_path_to_root(vol, cifs_sb,
cifs_sb_master_tcon(cifs_sb));
@@ -560,73 +559,32 @@ cifs_get_root(struct smb_vol *vol, struct super_block *sb)
xid = GetXid();
sep = CIFS_DIR_SEP(cifs_sb);
- dparent = dget(sb->s_root);
- full_len = strlen(full_path);
- full_path[full_len] = sep;
- pstart = full_path + 1;
-
- for (i = 1, len = 0; i <= full_len; i++) {
- if (full_path[i] != sep || !len) {
- len++;
- continue;
- }
-
- full_path[i] = 0;
- cFYI(1, "get dentry for %s", pstart);
-
- name.name = pstart;
- name.len = len;
- name.hash = full_name_hash(pstart, len);
- dchild = d_lookup(dparent, &name);
- if (dchild == NULL) {
- cFYI(1, "not exists");
- dchild = d_alloc(dparent, &name);
- if (dchild == NULL) {
- dput(dparent);
- dparent = ERR_PTR(-ENOMEM);
- goto out;
- }
- }
-
- cFYI(1, "get inode");
- if (dchild->d_inode == NULL) {
- cFYI(1, "not exists");
- inode = NULL;
- if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
- rc = cifs_get_inode_info_unix(&inode, full_path,
- sb, xid);
- else
- rc = cifs_get_inode_info(&inode, full_path,
- NULL, sb, xid, NULL);
- if (rc) {
- dput(dchild);
- dput(dparent);
- dparent = ERR_PTR(rc);
- goto out;
- }
- alias = d_materialise_unique(dchild, inode);
- if (alias != NULL) {
- dput(dchild);
- if (IS_ERR(alias)) {
- dput(dparent);
- dparent = ERR_PTR(-EINVAL); /* XXX */
- goto out;
- }
- dchild = alias;
- }
- }
- cFYI(1, "parent %p, child %p", dparent, dchild);
-
- dput(dparent);
- dparent = dchild;
- len = 0;
- pstart = full_path + i + 1;
- full_path[i] = sep;
- }
-out:
+ dentry = dget(sb->s_root);
+ p = s = full_path;
+
+ do {
+ struct inode *dir = dentry->d_inode;
+ struct dentry *child;
+
+ /* skip separators */
+ while (*s == sep)
+ s++;
+ if (!*s)
+ break;
+ p = s++;
+ /* next separator */
+ while (*s && *s != sep)
+ s++;
+
+ mutex_lock(&dir->i_mutex);
+ child = lookup_one_len(p, dentry, s - p);
+ mutex_unlock(&dir->i_mutex);
+ dput(dentry);
+ dentry = child;
+ } while (!IS_ERR(dentry));
_FreeXid(xid);
kfree(full_path);
- return dparent;
+ return dentry;
}
static int cifs_set_super(struct super_block *sb, void *data)
@@ -746,8 +704,11 @@ static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
static loff_t cifs_llseek(struct file *file, loff_t offset, int origin)
{
- /* origin == SEEK_END => we must revalidate the cached file length */
- if (origin == SEEK_END) {
+ /*
+ * origin == SEEK_END || SEEK_DATA || SEEK_HOLE => we must revalidate
+ * the cached file length
+ */
+ if (origin != SEEK_SET || origin != SEEK_CUR) {
int rc;
struct inode *inode = file->f_path.dentry->d_inode;
diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h
index 0900e1658c96..fbd050c8d52a 100644
--- a/fs/cifs/cifsfs.h
+++ b/fs/cifs/cifsfs.h
@@ -91,8 +91,8 @@ extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,
extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos);
extern int cifs_lock(struct file *, int, struct file_lock *);
-extern int cifs_fsync(struct file *, int);
-extern int cifs_strict_fsync(struct file *, int);
+extern int cifs_fsync(struct file *, loff_t, loff_t, int);
+extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int);
extern int cifs_flush(struct file *, fl_owner_t id);
extern int cifs_file_mmap(struct file * , struct vm_area_struct *);
extern int cifs_file_strict_mmap(struct file * , struct vm_area_struct *);
@@ -129,5 +129,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops;
#endif /* CIFS_NFSD_EXPORT */
-#define CIFS_VERSION "1.73"
+#define CIFS_VERSION "1.74"
#endif /* _CIFSFS_H */
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index dbd669cc5bc7..e66297bad412 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -320,9 +320,10 @@ requeue_echo:
}
static int
-cifs_demultiplex_thread(struct TCP_Server_Info *server)
+cifs_demultiplex_thread(void *p)
{
int length;
+ struct TCP_Server_Info *server = p;
unsigned int pdu_length, total_read;
struct smb_hdr *smb_buffer = NULL;
struct smb_hdr *bigbuf = NULL;
@@ -1791,7 +1792,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
* this will succeed. No need for try_module_get().
*/
__module_get(THIS_MODULE);
- tcp_ses->tsk = kthread_run((void *)(void *)cifs_demultiplex_thread,
+ tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
tcp_ses, "cifsd");
if (IS_ERR(tcp_ses->tsk)) {
rc = PTR_ERR(tcp_ses->tsk);
@@ -3485,7 +3486,7 @@ cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
goto out;
}
- snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid);
+ snprintf(username, sizeof(username), "krb50x%x", fsuid);
vol_info->username = username;
vol_info->local_nls = cifs_sb->local_nls;
vol_info->linux_uid = fsuid;
diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c
index 81914df47ef1..499f27fc8576 100644
--- a/fs/cifs/dir.c
+++ b/fs/cifs/dir.c
@@ -55,6 +55,7 @@ build_path_from_dentry(struct dentry *direntry)
char dirsep;
struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
+ unsigned seq;
if (direntry == NULL)
return NULL; /* not much we can do if dentry is freed and
@@ -68,22 +69,29 @@ build_path_from_dentry(struct dentry *direntry)
dfsplen = 0;
cifs_bp_rename_retry:
namelen = dfsplen;
+ seq = read_seqbegin(&rename_lock);
+ rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) {
namelen += (1 + temp->d_name.len);
temp = temp->d_parent;
if (temp == NULL) {
cERROR(1, "corrupt dentry");
+ rcu_read_unlock();
return NULL;
}
}
+ rcu_read_unlock();
full_path = kmalloc(namelen+1, GFP_KERNEL);
if (full_path == NULL)
return full_path;
full_path[namelen] = 0; /* trailing null */
+ rcu_read_lock();
for (temp = direntry; !IS_ROOT(temp);) {
+ spin_lock(&temp->d_lock);
namelen -= 1 + temp->d_name.len;
if (namelen < 0) {
+ spin_unlock(&temp->d_lock);
break;
} else {
full_path[namelen] = dirsep;
@@ -91,14 +99,17 @@ cifs_bp_rename_retry:
temp->d_name.len);
cFYI(0, "name: %s", full_path + namelen);
}
+ spin_unlock(&temp->d_lock);
temp = temp->d_parent;
if (temp == NULL) {
cERROR(1, "corrupt dentry");
+ rcu_read_unlock();
kfree(full_path);
return NULL;
}
}
- if (namelen != dfsplen) {
+ rcu_read_unlock();
+ if (namelen != dfsplen || read_seqretry(&rename_lock, seq)) {
cERROR(1, "did not end path lookup where expected namelen is %d",
namelen);
/* presumably this is only possible if racing with a rename
@@ -168,7 +179,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if (oplockEnabled)
oplock = REQ_OPLOCK;
- if (nd && (nd->flags & LOOKUP_OPEN))
+ if (nd)
oflags = nd->intent.open.file->f_flags;
else
oflags = O_RDONLY | O_CREAT;
@@ -203,7 +214,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
which should be rare for path not covered on files) */
}
- if (nd && (nd->flags & LOOKUP_OPEN)) {
+ if (nd) {
/* if the file is going to stay open, then we
need to set the desired access properly */
desiredAccess = 0;
@@ -317,7 +328,7 @@ cifs_create_set_dentry:
else
cFYI(1, "Create worked, get_inode_info failed rc = %d", rc);
- if (newinode && nd && (nd->flags & LOOKUP_OPEN)) {
+ if (newinode && nd) {
struct cifsFileInfo *pfile_info;
struct file *filp;
@@ -557,7 +568,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
* reduction in network traffic in the other paths.
*/
if (pTcon->unix_ext) {
- if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
+ if (nd && !(nd->flags & LOOKUP_DIRECTORY) &&
(nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
(nd->intent.open.file->f_flags & O_CREAT)) {
rc = cifs_posix_open(full_path, &newInode,
@@ -630,7 +641,7 @@ lookup_out:
static int
cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
{
- if (nd->flags & LOOKUP_RCU)
+ if (nd && (nd->flags & LOOKUP_RCU))
return -ECHILD;
if (direntry->d_inode) {
@@ -652,10 +663,8 @@ cifs_d_revalidate(struct dentry *direntry, struct nameidata *nd)
* case sensitive name which is specified by user if this is
* for creation.
*/
- if (!(nd->flags & (LOOKUP_CONTINUE | LOOKUP_PARENT))) {
- if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
- return 0;
- }
+ if (nd->flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
+ return 0;
if (time_after(jiffies, direntry->d_time + HZ) || !lookupCacheEnabled)
return 0;
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index bb71471a4d9d..378acdafa356 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -1401,7 +1401,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
return rc;
}
-int cifs_strict_fsync(struct file *file, int datasync)
+int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,
+ int datasync)
{
int xid;
int rc = 0;
@@ -1410,6 +1411,11 @@ int cifs_strict_fsync(struct file *file, int datasync)
struct inode *inode = file->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+ rc = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (rc)
+ return rc;
+ mutex_lock(&inode->i_mutex);
+
xid = GetXid();
cFYI(1, "Sync file - name: %s datasync: 0x%x",
@@ -1428,16 +1434,23 @@ int cifs_strict_fsync(struct file *file, int datasync)
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
FreeXid(xid);
+ mutex_unlock(&inode->i_mutex);
return rc;
}
-int cifs_fsync(struct file *file, int datasync)
+int cifs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
{
int xid;
int rc = 0;
struct cifs_tcon *tcon;
struct cifsFileInfo *smbfile = file->private_data;
struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
+ struct inode *inode = file->f_mapping->host;
+
+ rc = filemap_write_and_wait_range(inode->i_mapping, start, end);
+ if (rc)
+ return rc;
+ mutex_lock(&inode->i_mutex);
xid = GetXid();
@@ -1449,6 +1462,7 @@ int cifs_fsync(struct file *file, int datasync)
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
FreeXid(xid);
+ mutex_unlock(&inode->i_mutex);
return rc;
}
@@ -1737,7 +1751,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,
io_parms.pid = pid;
io_parms.tcon = pTcon;
io_parms.offset = *poffset;
- io_parms.length = len;
+ io_parms.length = cur_len;
rc = CIFSSMBRead(xid, &io_parms, &bytes_read,
&read_data, &buf_type);
pSMBr = (struct smb_com_read_rsp *)read_data;
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 6751e745bbc6..965a3af186a1 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -796,7 +796,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
file->f_pos++;
case 1:
if (filldir(direntry, "..", 2, file->f_pos,
- file->f_path.dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) {
+ parent_ino(file->f_path.dentry), DT_DIR) < 0) {
cERROR(1, "Filldir for parent dir failed");
rc = -ENOMEM;
break;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 3892ab817a36..d3e619692ee0 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -428,8 +428,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
flags |= NTLMSSP_NEGOTIATE_SIGN;
if (!ses->server->session_estab)
- flags |= NTLMSSP_NEGOTIATE_KEY_XCH |
- NTLMSSP_NEGOTIATE_EXTENDED_SEC;
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
}
sec_blob->NegotiateFlags = cpu_to_le32(flags);
@@ -465,10 +464,11 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC;
if (ses->server->sec_mode &
- (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
flags |= NTLMSSP_NEGOTIATE_SIGN;
- if (ses->server->sec_mode & SECMODE_SIGN_REQUIRED)
- flags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ if (!ses->server->session_estab)
+ flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
+ }
tmp = pbuffer + sizeof(AUTHENTICATE_MESSAGE);
sec_blob->NegotiateFlags = cpu_to_le32(flags);