Commit d2ecad9f authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6: (56 commits)
  [CIFS] move close processing  from cifs_close to cifsFileInfo_put
  cifs: convert cifs_tcp_ses_lock from a rwlock to a spinlock
  cifs: cancel_delayed_work() + flush_scheduled_work() -> cancel_delayed_work_sync()
  Clean up two declarations of blob_len
  cifs: move cifsFileInfo_put to file.c
  cifs: convert GlobalSMBSeslock from a rwlock to regular spinlock
  [CIFS] Fix minor checkpatch warning and update cifs version
  cifs: move cifs_new_fileinfo to file.c
  cifs: eliminate pfile pointer from cifsFileInfo
  cifs: cifs_write argument change and cleanup
  cifs: clean up cifs_reopen_file
  cifs: eliminate the inode argument from cifs_new_fileinfo
  cifs: eliminate oflags option from cifs_new_fileinfo
  cifs: fix flags handling in cifs_posix_open
  cifs: eliminate cifs_posix_open_inode_helper
  cifs: handle FindFirst failure gracefully
  NTLM authentication and signing - Calculate auth response per smb session
  cifs: don't use vfsmount to pin superblock for oplock breaks
  cifs: keep dentry reference in cifsFileInfo instead of inode reference
  cifs: on multiuser mount, set ownership to current_fsuid/current_fsgid (try #7)
  ...

Fix up trivial conflict in fs/cifs/cifsfs.c due to added/removed header files
parents c70b5296 cdff08e7
...@@ -527,6 +527,11 @@ A partial list of the supported mount options follows: ...@@ -527,6 +527,11 @@ A partial list of the supported mount options follows:
SFU does). In the future the bottom 9 bits of the SFU does). In the future the bottom 9 bits of the
mode also will be emulated using queries of the security mode also will be emulated using queries of the security
descriptor (ACL). descriptor (ACL).
mfsymlinks Enable support for Minshall+French symlinks
(see http://wiki.samba.org/index.php/UNIX_Extensions#Minshall.2BFrench_symlinks)
This option is ignored when specified together with the
'sfu' option. Minshall+French symlinks are used even if
the server supports the CIFS Unix Extensions.
sign Must use packet signing (helps avoid unwanted data modification sign Must use packet signing (helps avoid unwanted data modification
by intermediate systems in the route). Note that signing by intermediate systems in the route). Note that signing
does not work with lanman or plaintext authentication. does not work with lanman or plaintext authentication.
......
...@@ -148,7 +148,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -148,7 +148,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
seq_printf(m, "Servers:"); seq_printf(m, "Servers:");
i = 0; i = 0;
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
...@@ -230,7 +230,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -230,7 +230,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n'); seq_putc(m, '\n');
/* BB add code to dump additional info such as TCP session info now */ /* BB add code to dump additional info such as TCP session info now */
...@@ -270,7 +270,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -270,7 +270,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
atomic_set(&totBufAllocCount, 0); atomic_set(&totBufAllocCount, 0);
atomic_set(&totSmBufAllocCount, 0); atomic_set(&totSmBufAllocCount, 0);
#endif /* CONFIG_CIFS_STATS2 */ #endif /* CONFIG_CIFS_STATS2 */
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
...@@ -303,7 +303,7 @@ static ssize_t cifs_stats_proc_write(struct file *file, ...@@ -303,7 +303,7 @@ static ssize_t cifs_stats_proc_write(struct file *file,
} }
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
} }
return count; return count;
...@@ -343,7 +343,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -343,7 +343,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
GlobalCurrentXid, GlobalMaxActiveXid); GlobalCurrentXid, GlobalMaxActiveXid);
i = 0; i = 0;
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp1, &cifs_tcp_ses_list) { list_for_each(tmp1, &cifs_tcp_ses_list) {
server = list_entry(tmp1, struct TCP_Server_Info, server = list_entry(tmp1, struct TCP_Server_Info,
tcp_ses_list); tcp_ses_list);
...@@ -397,7 +397,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v) ...@@ -397,7 +397,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
} }
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
seq_putc(m, '\n'); seq_putc(m, '\n');
return 0; return 0;
......
...@@ -44,8 +44,7 @@ static void cifs_dfs_expire_automounts(struct work_struct *work) ...@@ -44,8 +44,7 @@ static void cifs_dfs_expire_automounts(struct work_struct *work)
void cifs_dfs_release_automount_timer(void) void cifs_dfs_release_automount_timer(void)
{ {
BUG_ON(!list_empty(&cifs_dfs_automount_list)); BUG_ON(!list_empty(&cifs_dfs_automount_list));
cancel_delayed_work(&cifs_dfs_automount_task); cancel_delayed_work_sync(&cifs_dfs_automount_task);
flush_scheduled_work();
} }
/** /**
...@@ -306,6 +305,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -306,6 +305,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
int xid, i; int xid, i;
int rc = 0; int rc = 0;
struct vfsmount *mnt = ERR_PTR(-ENOENT); struct vfsmount *mnt = ERR_PTR(-ENOENT);
struct tcon_link *tlink;
cFYI(1, "in %s", __func__); cFYI(1, "in %s", __func__);
BUG_ON(IS_ROOT(dentry)); BUG_ON(IS_ROOT(dentry));
...@@ -315,14 +315,6 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -315,14 +315,6 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
dput(nd->path.dentry); dput(nd->path.dentry);
nd->path.dentry = dget(dentry); nd->path.dentry = dget(dentry);
cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
ses = cifs_sb->tcon->ses;
if (!ses) {
rc = -EINVAL;
goto out_err;
}
/* /*
* The MSDFS spec states that paths in DFS referral requests and * The MSDFS spec states that paths in DFS referral requests and
* responses must be prefixed by a single '\' character instead of * responses must be prefixed by a single '\' character instead of
...@@ -335,10 +327,20 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) ...@@ -335,10 +327,20 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
goto out_err; goto out_err;
} }
rc = get_dfs_path(xid, ses , full_path + 1, cifs_sb->local_nls, cifs_sb = CIFS_SB(dentry->d_inode->i_sb);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
goto out_err;
}
ses = tlink_tcon(tlink)->ses;
rc = get_dfs_path(xid, ses, full_path + 1, cifs_sb->local_nls,
&num_referrals, &referrals, &num_referrals, &referrals,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
for (i = 0; i < num_referrals; i++) { for (i = 0; i < num_referrals; i++) {
int len; int len;
dump_referral(referrals+i); dump_referral(referrals+i);
......
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
* the GNU Lesser General Public License for more details. * the GNU Lesser General Public License for more details.
* *
*/ */
#include <linux/radix-tree.h>
#ifndef _CIFS_FS_SB_H #ifndef _CIFS_FS_SB_H
#define _CIFS_FS_SB_H #define _CIFS_FS_SB_H
...@@ -36,23 +38,28 @@ ...@@ -36,23 +38,28 @@
#define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */ #define CIFS_MOUNT_NOPOSIXBRL 0x2000 /* mandatory not posix byte range lock */
#define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/ #define CIFS_MOUNT_NOSSYNC 0x4000 /* don't do slow SMBflush on every sync*/
#define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */ #define CIFS_MOUNT_FSCACHE 0x8000 /* local caching enabled */
#define CIFS_MOUNT_MF_SYMLINKS 0x10000 /* Minshall+French Symlinks enabled */
#define CIFS_MOUNT_MULTIUSER 0x20000 /* multiuser mount */
struct cifs_sb_info { struct cifs_sb_info {
struct cifsTconInfo *tcon; /* primary mount */ struct radix_tree_root tlink_tree;
struct list_head nested_tcon_q; #define CIFS_TLINK_MASTER_TAG 0 /* is "master" (mount) tcon */
spinlock_t tlink_tree_lock;
struct nls_table *local_nls; struct nls_table *local_nls;
unsigned int rsize; unsigned int rsize;
unsigned int wsize; unsigned int wsize;
atomic_t active;
uid_t mnt_uid; uid_t mnt_uid;
gid_t mnt_gid; gid_t mnt_gid;
mode_t mnt_file_mode; mode_t mnt_file_mode;
mode_t mnt_dir_mode; mode_t mnt_dir_mode;
int mnt_cifs_flags; unsigned int mnt_cifs_flags;
int prepathlen; int prepathlen;
char *prepath; /* relative path under the share to mount to */ char *prepath; /* relative path under the share to mount to */
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
char *mountdata; /* mount options received at mount time */ char *mountdata; /* mount options received at mount time */
#endif #endif
struct backing_dev_info bdi; struct backing_dev_info bdi;
struct delayed_work prune_tlinks;
}; };
#endif /* _CIFS_FS_SB_H */ #endif /* _CIFS_FS_SB_H */
...@@ -557,11 +557,16 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, ...@@ -557,11 +557,16 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
{ {
struct cifs_ntsd *pntsd = NULL; struct cifs_ntsd *pntsd = NULL;
int xid, rc; int xid, rc;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return NULL;
xid = GetXid(); xid = GetXid();
rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen);
return pntsd; return pntsd;
...@@ -574,10 +579,16 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, ...@@ -574,10 +579,16 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
int oplock = 0; int oplock = 0;
int xid, rc; int xid, rc;
__u16 fid; __u16 fid;
struct cifsTconInfo *tcon;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return NULL;
tcon = tlink_tcon(tlink);
xid = GetXid(); xid = GetXid();
rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, READ_CONTROL, 0, rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, 0,
&fid, &oplock, NULL, cifs_sb->local_nls, &fid, &oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) { if (rc) {
...@@ -585,11 +596,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, ...@@ -585,11 +596,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
goto out; goto out;
} }
rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen); rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen);
cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen); cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen);
CIFSSMBClose(xid, cifs_sb->tcon, fid); CIFSSMBClose(xid, tcon, fid);
out: out:
cifs_put_tlink(tlink);
FreeXid(xid); FreeXid(xid);
return pntsd; return pntsd;
} }
...@@ -603,7 +615,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb, ...@@ -603,7 +615,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
struct cifsFileInfo *open_file = NULL; struct cifsFileInfo *open_file = NULL;
if (inode) if (inode)
open_file = find_readable_file(CIFS_I(inode)); open_file = find_readable_file(CIFS_I(inode), true);
if (!open_file) if (!open_file)
return get_cifs_acl_by_path(cifs_sb, path, pacllen); return get_cifs_acl_by_path(cifs_sb, path, pacllen);
...@@ -616,10 +628,15 @@ static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid, ...@@ -616,10 +628,15 @@ static int set_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, __u16 fid,
struct cifs_ntsd *pnntsd, u32 acllen) struct cifs_ntsd *pnntsd, u32 acllen)
{ {
int xid, rc; int xid, rc;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
xid = GetXid(); xid = GetXid();
rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
cFYI(DBG2, "SetCIFSACL rc = %d", rc); cFYI(DBG2, "SetCIFSACL rc = %d", rc);
return rc; return rc;
...@@ -631,10 +648,16 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, ...@@ -631,10 +648,16 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
int oplock = 0; int oplock = 0;
int xid, rc; int xid, rc;
__u16 fid; __u16 fid;
struct cifsTconInfo *tcon;
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
xid = GetXid(); xid = GetXid();
rc = CIFSSMBOpen(xid, cifs_sb->tcon, path, FILE_OPEN, WRITE_DAC, 0, rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, WRITE_DAC, 0,
&fid, &oplock, NULL, cifs_sb->local_nls, &fid, &oplock, NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc) { if (rc) {
...@@ -642,12 +665,13 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path, ...@@ -642,12 +665,13 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
goto out; goto out;
} }
rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen); rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
cFYI(DBG2, "SetCIFSACL rc = %d", rc); cFYI(DBG2, "SetCIFSACL rc = %d", rc);
CIFSSMBClose(xid, cifs_sb->tcon, fid); CIFSSMBClose(xid, tcon, fid);
out: out:
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -661,7 +685,7 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen, ...@@ -661,7 +685,7 @@ static int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,
cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode); cFYI(DBG2, "set ACL for %s from mode 0x%x", path, inode->i_mode);
open_file = find_readable_file(CIFS_I(inode)); open_file = find_readable_file(CIFS_I(inode), true);
if (!open_file) if (!open_file)
return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen); return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "md5.h" #include "md5.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "cifsproto.h" #include "cifsproto.h"
#include "ntlmssp.h"
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/random.h> #include <linux/random.h>
...@@ -42,7 +43,7 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8, ...@@ -42,7 +43,7 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
unsigned char *p24); unsigned char *p24);
static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu, static int cifs_calculate_signature(const struct smb_hdr *cifs_pdu,
const struct mac_key *key, char *signature) const struct session_key *key, char *signature)
{ {
struct MD5Context context; struct MD5Context context;
...@@ -78,7 +79,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, ...@@ -78,7 +79,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
server->sequence_number++; server->sequence_number++;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
rc = cifs_calculate_signature(cifs_pdu, &server->mac_signing_key, rc = cifs_calculate_signature(cifs_pdu, &server->session_key,
smb_signature); smb_signature);
if (rc) if (rc)
memset(cifs_pdu->Signature.SecuritySignature, 0, 8); memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
...@@ -89,7 +90,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server, ...@@ -89,7 +90,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
} }
static int cifs_calc_signature2(const struct kvec *iov, int n_vec, static int cifs_calc_signature2(const struct kvec *iov, int n_vec,
const struct mac_key *key, char *signature) const struct session_key *key, char *signature)
{ {
struct MD5Context context; struct MD5Context context;
int i; int i;
...@@ -145,7 +146,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, ...@@ -145,7 +146,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
server->sequence_number++; server->sequence_number++;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
rc = cifs_calc_signature2(iov, n_vec, &server->mac_signing_key, rc = cifs_calc_signature2(iov, n_vec, &server->session_key,
smb_signature); smb_signature);
if (rc) if (rc)
memset(cifs_pdu->Signature.SecuritySignature, 0, 8); memset(cifs_pdu->Signature.SecuritySignature, 0, 8);
...@@ -156,14 +157,14 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server, ...@@ -156,14 +157,14 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
} }
int cifs_verify_signature(struct smb_hdr *cifs_pdu, int cifs_verify_signature(struct smb_hdr *cifs_pdu,
const struct mac_key *mac_key, const struct session_key *session_key,
__u32 expected_sequence_number) __u32 expected_sequence_number)
{ {
unsigned int rc; unsigned int rc;
char server_response_sig[8]; char server_response_sig[8];
char what_we_think_sig_should_be[20]; char what_we_think_sig_should_be[20];
if ((cifs_pdu == NULL) || (mac_key == NULL)) if (cifs_pdu == NULL || session_key == NULL)
return -EINVAL; return -EINVAL;
if (cifs_pdu->Command == SMB_COM_NEGOTIATE) if (cifs_pdu->Command == SMB_COM_NEGOTIATE)
...@@ -192,7 +193,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, ...@@ -192,7 +193,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,
cpu_to_le32(expected_sequence_number); cpu_to_le32(expected_sequence_number);
cifs_pdu->Signature.Sequence.Reserved = 0; cifs_pdu->Signature.Sequence.Reserved = 0;
rc = cifs_calculate_signature(cifs_pdu, mac_key, rc = cifs_calculate_signature(cifs_pdu, session_key,
what_we_think_sig_should_be); what_we_think_sig_should_be);
if (rc) if (rc)
...@@ -209,7 +210,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu, ...@@ -209,7 +210,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,
} }
/* We fill in key by putting in 40 byte array which was allocated by caller */ /* We fill in key by putting in 40 byte array which was allocated by caller */
int cifs_calculate_mac_key(struct mac_key *key, const char *rn, int cifs_calculate_session_key(struct session_key *key, const char *rn,
const char *password) const char *password)
{ {
char temp_key[16]; char temp_key[16];
...@@ -262,6 +263,148 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt, ...@@ -262,6 +263,148 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
} }
#endif /* CIFS_WEAK_PW_HASH */ #endif /* CIFS_WEAK_PW_HASH */
/* Build a proper attribute value/target info pairs blob.
* Fill in netbios and dns domain name and workstation name
* and client time (total five av pairs and + one end of fields indicator.
* Allocate domain name which gets freed when session struct is deallocated.
*/
static int
build_avpair_blob(struct cifsSesInfo *ses, const struct nls_table *nls_cp)
{
unsigned int dlen;
unsigned int wlen;
unsigned int size = 6 * sizeof(struct ntlmssp2_name);
__le64 curtime;
char *defdmname = "WORKGROUP";
unsigned char *blobptr;
struct ntlmssp2_name *attrptr;
if (!ses->domainName) {
ses->domainName = kstrdup(defdmname, GFP_KERNEL);
if (!ses->domainName)
return -ENOMEM;
}
dlen = strlen(ses->domainName);
wlen = strlen(ses->server->hostname);
/* The length of this blob is a size which is
* six times the size of a structure which holds name/size +
* two times the unicode length of a domain name +
* two times the unicode length of a server name +
* size of a timestamp (which is 8 bytes).
*/
ses->tilen = size + 2 * (2 * dlen) + 2 * (2 * wlen) + 8;
ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL);
if (!ses->tiblob) {
ses->tilen = 0;
cERROR(1, "Challenge target info allocation failure");
return -ENOMEM;
}
blobptr = ses->tiblob;
attrptr = (struct ntlmssp2_name *) blobptr;
attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_DOMAIN_NAME);
attrptr->length = cpu_to_le16(2 * dlen);
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
blobptr += 2 * dlen;
attrptr = (struct ntlmssp2_name *) blobptr;
attrptr->type = cpu_to_le16(NTLMSSP_AV_NB_COMPUTER_NAME);
attrptr->length = cpu_to_le16(2 * wlen);
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp);
blobptr += 2 * wlen;
attrptr = (struct ntlmssp2_name *) blobptr;
attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_DOMAIN_NAME);
attrptr->length = cpu_to_le16(2 * dlen);
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUCS((__le16 *)blobptr, ses->domainName, dlen, nls_cp);
blobptr += 2 * dlen;
attrptr = (struct ntlmssp2_name *) blobptr;
attrptr->type = cpu_to_le16(NTLMSSP_AV_DNS_COMPUTER_NAME);
attrptr->length = cpu_to_le16(2 * wlen);
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
cifs_strtoUCS((__le16 *)blobptr, ses->server->hostname, wlen, nls_cp);
blobptr += 2 * wlen;
attrptr = (struct ntlmssp2_name *) blobptr;
attrptr->type = cpu_to_le16(NTLMSSP_AV_TIMESTAMP);
attrptr->length = cpu_to_le16(sizeof(__le64));
blobptr = (unsigned char *)attrptr + sizeof(struct ntlmssp2_name);
curtime = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
memcpy(blobptr, &curtime, sizeof(__le64));
return 0;
}
/* Server has provided av pairs/target info in the type 2 challenge
* packet and we have plucked it and stored within smb session.
* We parse that blob here to find netbios domain name to be used
* as part of ntlmv2 authentication (in Target String), if not already
* specified on the command line.
* If this function returns without any error but without fetching
* domain name, authentication may fail against some server but
* may not fail against other (those who are not very particular
* about target string i.e. for some, just user name might suffice.
*/
static int
find_domain_name(struct cifsSesInfo *ses)
{
unsigned int attrsize;
unsigned int type;
unsigned int onesize = sizeof(struct ntlmssp2_name);
unsigned char *blobptr;
unsigned char *blobend;
struct ntlmssp2_name *attrptr;
if (!ses->tilen || !ses->tiblob)
return 0;
blobptr = ses->tiblob;
blobend = ses->tiblob + ses->tilen;
while (blobptr + onesize < blobend) {
attrptr = (struct ntlmssp2_name *) blobptr;
type = le16_to_cpu(attrptr->type);
if (type == NTLMSSP_AV_EOL)
break;
blobptr += 2; /* advance attr type */
attrsize = le16_to_cpu(attrptr->length);
blobptr += 2; /* advance attr size */
if (blobptr + attrsize > blobend)
break;
if (type == NTLMSSP_AV_NB_DOMAIN_NAME) {
if (!attrsize)
break;
if (!ses->domainName) {
struct nls_table *default_nls;
ses->domainName =
kmalloc(attrsize + 1, GFP_KERNEL);
if (!ses->domainName)
return -ENOMEM;
default_nls = load_nls_default();
cifs_from_ucs2(ses->domainName,
(__le16 *)blobptr, attrsize, attrsize,
default_nls, false);
unload_nls(default_nls);
break;
}
}
blobptr += attrsize; /* advance attr value */
}
return 0;
}
static int calc_ntlmv2_hash(struct cifsSesInfo *ses, static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
...@@ -315,13 +458,14 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses, ...@@ -315,13 +458,14 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
calc_exit_2: calc_exit_2:
/* BB FIXME what about bytes 24 through 40 of the signing key? /* BB FIXME what about bytes 24 through 40 of the signing key?
compare with the NTLM example */ compare with the NTLM example */
hmac_md5_final(ses->server->ntlmv2_hash, pctxt); hmac_md5_final(ses->ntlmv2_hash, pctxt);
kfree(pctxt); kfree(pctxt);
return rc; return rc;
} }
void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, int
setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc; int rc;
...@@ -333,25 +477,48 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf, ...@@ -333,25 +477,48 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
buf->reserved2 = 0; buf->reserved2 = 0;
buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
buf->names[0].length = 0; if (ses->server->secType == RawNTLMSSP) {
buf->names[1].type = 0; if (!ses->domainName) {
buf->names[1].length = 0; rc = find_domain_name(ses);
if (rc) {
cERROR(1, "error %d finding domain name", rc);
goto setup_ntlmv2_rsp_ret;
}
}
} else {
rc = build_avpair_blob(ses, nls_cp);
if (rc) {
cERROR(1, "error %d building av pair blob", rc);
return rc;
}
}
/* calculate buf->ntlmv2_hash */ /* calculate buf->ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, nls_cp); rc = calc_ntlmv2_hash(ses, nls_cp);
if (rc) if (rc) {
cERROR(1, "could not get v2 hash rc %d", rc); cERROR(1, "could not get v2 hash rc %d", rc);
goto setup_ntlmv2_rsp_ret;
}
CalcNTLMv2_response(ses, resp_buf); CalcNTLMv2_response(ses, resp_buf);
/* now calculate the MAC key for NTLMv2 */ /* now calculate the session key for NTLMv2 */
hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context);
hmac_md5_update(resp_buf, 16, &context); hmac_md5_update(resp_buf, 16, &context);
hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context); hmac_md5_final(ses->auth_key.data.ntlmv2.key, &context);
memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf, memcpy(&ses->auth_key.data.ntlmv2.resp, resp_buf,
sizeof(struct ntlmv2_resp)); sizeof(struct ntlmv2_resp));
ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp); ses->auth_key.len = 16 + sizeof(struct ntlmv2_resp);
return 0;
setup_ntlmv2_rsp_ret:
kfree(ses->tiblob);
ses->tiblob = NULL;
ses->tilen = 0;
return rc;
} }
void CalcNTLMv2_response(const struct cifsSesInfo *ses, void CalcNTLMv2_response(const struct cifsSesInfo *ses,
...@@ -359,12 +526,15 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses, ...@@ -359,12 +526,15 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses,
{ {
struct HMACMD5Context context; struct HMACMD5Context context;
/* rest of v2 struct already generated */ /* rest of v2 struct already generated */
memcpy(v2_session_response + 8, ses->server->cryptKey, 8); memcpy(v2_session_response + 8, ses->cryptKey, 8);
hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context); hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context);
hmac_md5_update(v2_session_response+8, hmac_md5_update(v2_session_response+8,
sizeof(struct ntlmv2_resp) - 8, &context); sizeof(struct ntlmv2_resp) - 8, &context);
if (ses->tilen)
hmac_md5_update(ses->tiblob, ses->tilen, &context);
hmac_md5_final(v2_session_response, &context); hmac_md5_final(v2_session_response, &context);
/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */ /* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
} }
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <net/ipv6.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
#define DECLARE_GLOBALS_HERE #define DECLARE_GLOBALS_HERE
...@@ -81,6 +82,24 @@ extern mempool_t *cifs_sm_req_poolp; ...@@ -81,6 +82,24 @@ extern mempool_t *cifs_sm_req_poolp;
extern mempool_t *cifs_req_poolp; extern mempool_t *cifs_req_poolp;
extern mempool_t *cifs_mid_poolp; extern mempool_t *cifs_mid_poolp;
void
cifs_sb_active(struct super_block *sb)
{
struct cifs_sb_info *server = CIFS_SB(sb);
if (atomic_inc_return(&server->active) == 1)
atomic_inc(&sb->s_active);
}
void
cifs_sb_deactive(struct super_block *sb)
{
struct cifs_sb_info *server = CIFS_SB(sb);
if (atomic_dec_and_test(&server->active))
deactivate_super(sb);
}
static int static int
cifs_read_super(struct super_block *sb, void *data, cifs_read_super(struct super_block *sb, void *data,
const char *devname, int silent) const char *devname, int silent)
...@@ -96,6 +115,9 @@ cifs_read_super(struct super_block *sb, void *data, ...@@ -96,6 +115,9 @@ cifs_read_super(struct super_block *sb, void *data,
if (cifs_sb == NULL) if (cifs_sb == NULL)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&cifs_sb->tlink_tree_lock);
INIT_RADIX_TREE(&cifs_sb->tlink_tree, GFP_KERNEL);
rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY); rc = bdi_setup_and_register(&cifs_sb->bdi, "cifs", BDI_CAP_MAP_COPY);
if (rc) { if (rc) {
kfree(cifs_sb); kfree(cifs_sb);
...@@ -135,9 +157,6 @@ cifs_read_super(struct super_block *sb, void *data, ...@@ -135,9 +157,6 @@ cifs_read_super(struct super_block *sb, void *data,
sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_magic = CIFS_MAGIC_NUMBER;
sb->s_op = &cifs_super_ops; sb->s_op = &cifs_super_ops;
sb->s_bdi = &cifs_sb->bdi; sb->s_bdi = &cifs_sb->bdi;
/* if (cifs_sb->tcon->ses->server->maxBuf > MAX_CIFS_HDR_SIZE + 512)
sb->s_blocksize =
cifs_sb->tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE; */
sb->s_blocksize = CIFS_MAX_MSGSIZE; sb->s_blocksize = CIFS_MAX_MSGSIZE;
sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */ sb->s_blocksize_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
inode = cifs_root_iget(sb, ROOT_I); inode = cifs_root_iget(sb, ROOT_I);
...@@ -219,7 +238,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -219,7 +238,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct super_block *sb = dentry->d_sb; struct super_block *sb = dentry->d_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *tcon = cifs_sb->tcon; struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
int rc = -EOPNOTSUPP; int rc = -EOPNOTSUPP;
int xid; int xid;
...@@ -361,14 +380,36 @@ static int ...@@ -361,14 +380,36 @@ static int
cifs_show_options(struct seq_file *s, struct vfsmount *m) cifs_show_options(struct seq_file *s, struct vfsmount *m)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(m->mnt_sb);
struct cifsTconInfo *tcon = cifs_sb->tcon; struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
struct sockaddr *srcaddr;
srcaddr = (struct sockaddr *)&tcon->ses->server->srcaddr;
seq_printf(s, ",unc=%s", tcon->treeName); seq_printf(s, ",unc=%s", tcon->treeName);
if (tcon->ses->userName)
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER)
seq_printf(s, ",multiuser");
else if (tcon->ses->userName)
seq_printf(s, ",username=%s", tcon->ses->userName); seq_printf(s, ",username=%s", tcon->ses->userName);
if (tcon->ses->domainName) if (tcon->ses->domainName)
seq_printf(s, ",domain=%s", tcon->ses->domainName); seq_printf(s, ",domain=%s", tcon->ses->domainName);
if (srcaddr->sa_family != AF_UNSPEC) {
struct sockaddr_in *saddr4;
struct sockaddr_in6 *saddr6;
saddr4 = (struct sockaddr_in *)srcaddr;
saddr6 = (struct sockaddr_in6 *)srcaddr;
if (srcaddr->sa_family == AF_INET6)
seq_printf(s, ",srcaddr=%pI6c",
&saddr6->sin6_addr);
else if (srcaddr->sa_family == AF_INET)
seq_printf(s, ",srcaddr=%pI4",
&saddr4->sin_addr.s_addr);
else
seq_printf(s, ",srcaddr=BAD-AF:%i",
(int)(srcaddr->sa_family));
}
seq_printf(s, ",uid=%d", cifs_sb->mnt_uid); seq_printf(s, ",uid=%d", cifs_sb->mnt_uid);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
seq_printf(s, ",forceuid"); seq_printf(s, ",forceuid");
...@@ -417,6 +458,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m) ...@@ -417,6 +458,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
seq_printf(s, ",dynperm"); seq_printf(s, ",dynperm");
if (m->mnt_sb->s_flags & MS_POSIXACL) if (m->mnt_sb->s_flags & MS_POSIXACL)
seq_printf(s, ",acl"); seq_printf(s, ",acl");
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
seq_printf(s, ",mfsymlinks");
seq_printf(s, ",rsize=%d", cifs_sb->rsize); seq_printf(s, ",rsize=%d", cifs_sb->rsize);
seq_printf(s, ",wsize=%d", cifs_sb->wsize); seq_printf(s, ",wsize=%d", cifs_sb->wsize);
...@@ -432,20 +475,18 @@ static void cifs_umount_begin(struct super_block *sb) ...@@ -432,20 +475,18 @@ static void cifs_umount_begin(struct super_block *sb)
if (cifs_sb == NULL) if (cifs_sb == NULL)
return; return;
tcon = cifs_sb->tcon; tcon = cifs_sb_master_tcon(cifs_sb);
if (tcon == NULL)
return;
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) { if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
/* we have other mounts to same share or we have /* we have other mounts to same share or we have
already tried to force umount this and woken up already tried to force umount this and woken up
all waiting network requests, nothing to do */ all waiting network requests, nothing to do */
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return; return;
} else if (tcon->tc_count == 1) } else if (tcon->tc_count == 1)
tcon->tidStatus = CifsExiting; tcon->tidStatus = CifsExiting;
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
/* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
/* cancel_notify_requests(tcon); */ /* cancel_notify_requests(tcon); */
...@@ -565,6 +606,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) ...@@ -565,6 +606,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
/* note that this is called by vfs setlease with lock_flocks held /* note that this is called by vfs setlease with lock_flocks held
to protect *lease from going away */ to protect *lease from going away */
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct cifsFileInfo *cfile = file->private_data;
if (!(S_ISREG(inode->i_mode))) if (!(S_ISREG(inode->i_mode)))
return -EINVAL; return -EINVAL;
...@@ -575,7 +617,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease) ...@@ -575,7 +617,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
((arg == F_WRLCK) && ((arg == F_WRLCK) &&
(CIFS_I(inode)->clientCanCacheAll))) (CIFS_I(inode)->clientCanCacheAll)))
return generic_setlease(file, arg, lease); return generic_setlease(file, arg, lease);
else if (CIFS_SB(inode->i_sb)->tcon->local_lease && else if (tlink_tcon(cfile->tlink)->local_lease &&
!CIFS_I(inode)->clientCanCacheRead) !CIFS_I(inode)->clientCanCacheRead)
/* If the server claims to support oplock on this /* If the server claims to support oplock on this
file, then we still need to check oplock even file, then we still need to check oplock even
...@@ -895,8 +937,8 @@ init_cifs(void) ...@@ -895,8 +937,8 @@ init_cifs(void)
GlobalTotalActiveXid = 0; GlobalTotalActiveXid = 0;
GlobalMaxActiveXid = 0; GlobalMaxActiveXid = 0;
memset(Local_System_Name, 0, 15); memset(Local_System_Name, 0, 15);
rwlock_init(&GlobalSMBSeslock); spin_lock_init(&cifs_tcp_ses_lock);
rwlock_init(&cifs_tcp_ses_lock); spin_lock_init(&cifs_file_list_lock);
spin_lock_init(&GlobalMid_Lock); spin_lock_init(&GlobalMid_Lock);
if (cifs_max_pending < 2) { if (cifs_max_pending < 2) {
...@@ -909,11 +951,11 @@ init_cifs(void) ...@@ -909,11 +951,11 @@ init_cifs(void)
rc = cifs_fscache_register(); rc = cifs_fscache_register();
if (rc) if (rc)
goto out; goto out_clean_proc;
rc = cifs_init_inodecache(); rc = cifs_init_inodecache();
if (rc) if (rc)
goto out_clean_proc; goto out_unreg_fscache;
rc = cifs_init_mids(); rc = cifs_init_mids();
if (rc) if (rc)
...@@ -935,19 +977,19 @@ init_cifs(void) ...@@ -935,19 +977,19 @@ init_cifs(void)
return 0; return 0;
#ifdef CONFIG_CIFS_UPCALL #ifdef CONFIG_CIFS_UPCALL
out_unregister_filesystem: out_unregister_filesystem:
unregister_filesystem(&cifs_fs_type); unregister_filesystem(&cifs_fs_type);
#endif #endif
out_destroy_request_bufs: out_destroy_request_bufs:
cifs_destroy_request_bufs(); cifs_destroy_request_bufs();
out_destroy_mids: out_destroy_mids:
cifs_destroy_mids(); cifs_destroy_mids();
out_destroy_inodecache: out_destroy_inodecache:
cifs_destroy_inodecache(); cifs_destroy_inodecache();
out_clean_proc: out_unreg_fscache:
cifs_proc_clean();
cifs_fscache_unregister(); cifs_fscache_unregister();
out: out_clean_proc:
cifs_proc_clean();
return rc; return rc;
} }
......
...@@ -42,10 +42,8 @@ extern const struct address_space_operations cifs_addr_ops; ...@@ -42,10 +42,8 @@ extern const struct address_space_operations cifs_addr_ops;
extern const struct address_space_operations cifs_addr_ops_smallbuf; extern const struct address_space_operations cifs_addr_ops_smallbuf;
/* Functions related to super block operations */ /* Functions related to super block operations */
/* extern const struct super_operations cifs_super_ops;*/ extern void cifs_sb_active(struct super_block *sb);
extern void cifs_read_inode(struct inode *); extern void cifs_sb_deactive(struct super_block *sb);
/*extern void cifs_delete_inode(struct inode *);*/ /* BB not needed yet */
/* extern void cifs_write_inode(struct inode *); */ /* BB not needed yet */
/* Functions related to inodes */ /* Functions related to inodes */
extern const struct inode_operations cifs_dir_inode_ops; extern const struct inode_operations cifs_dir_inode_ops;
...@@ -114,5 +112,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); ...@@ -114,5 +112,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);
extern const struct export_operations cifs_export_ops; extern const struct export_operations cifs_export_ops;
#endif /* EXPERIMENTAL */ #endif /* EXPERIMENTAL */
#define CIFS_VERSION "1.65" #define CIFS_VERSION "1.67"
#endif /* _CIFSFS_H */ #endif /* _CIFSFS_H */
...@@ -97,7 +97,7 @@ enum protocolEnum { ...@@ -97,7 +97,7 @@ enum protocolEnum {
/* Netbios frames protocol not supported at this time */ /* Netbios frames protocol not supported at this time */
}; };
struct mac_key { struct session_key {
unsigned int len; unsigned int len;
union { union {
char ntlm[CIFS_SESS_KEY_SIZE + 16]; char ntlm[CIFS_SESS_KEY_SIZE + 16];
...@@ -139,6 +139,7 @@ struct TCP_Server_Info { ...@@ -139,6 +139,7 @@ struct TCP_Server_Info {
struct sockaddr_in sockAddr; struct sockaddr_in sockAddr;
struct sockaddr_in6 sockAddr6; struct sockaddr_in6 sockAddr6;
} addr; } addr;
struct sockaddr_storage srcaddr; /* locally bind to this IP */
wait_queue_head_t response_q; wait_queue_head_t response_q;
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/ wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
struct list_head pending_mid_q; struct list_head pending_mid_q;
...@@ -178,12 +179,10 @@ struct TCP_Server_Info { ...@@ -178,12 +179,10 @@ struct TCP_Server_Info {
int capabilities; /* allow selective disabling of caps by smb sess */ int capabilities; /* allow selective disabling of caps by smb sess */
int timeAdj; /* Adjust for difference in server time zone in sec */ int timeAdj; /* Adjust for difference in server time zone in sec */
__u16 CurrentMid; /* multiplex id - rotating counter */ __u16 CurrentMid; /* multiplex id - rotating counter */
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
/* 16th byte of RFC1001 workstation name is always null */ /* 16th byte of RFC1001 workstation name is always null */
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
__u32 sequence_number; /* needed for CIFS PDU signature */ __u32 sequence_number; /* needed for CIFS PDU signature */
struct mac_key mac_signing_key; struct session_key session_key;
char ntlmv2_hash[16];
unsigned long lstrp; /* when we got last response from this server */ unsigned long lstrp; /* when we got last response from this server */
u16 dialect; /* dialect index that server chose */ u16 dialect; /* dialect index that server chose */
/* extended security flavors that server supports */ /* extended security flavors that server supports */
...@@ -191,6 +190,7 @@ struct TCP_Server_Info { ...@@ -191,6 +190,7 @@ struct TCP_Server_Info {
bool sec_mskerberos; /* supports legacy MS Kerberos */ bool sec_mskerberos; /* supports legacy MS Kerberos */
bool sec_kerberosu2u; /* supports U2U Kerberos */ bool sec_kerberosu2u; /* supports U2U Kerberos */
bool sec_ntlmssp; /* supports NTLMSSP */ bool sec_ntlmssp; /* supports NTLMSSP */
bool session_estab; /* mark when very first sess is established */
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
struct fscache_cookie *fscache; /* client index cache cookie */ struct fscache_cookie *fscache; /* client index cache cookie */
#endif #endif
...@@ -222,6 +222,11 @@ struct cifsSesInfo { ...@@ -222,6 +222,11 @@ struct cifsSesInfo {
char userName[MAX_USERNAME_SIZE + 1]; char userName[MAX_USERNAME_SIZE + 1];
char *domainName; char *domainName;
char *password; char *password;
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
struct session_key auth_key;
char ntlmv2_hash[16];
unsigned int tilen; /* length of the target info blob */
unsigned char *tiblob; /* target info blob in challenge response */
bool need_reconnect:1; /* connection reset, uid now invalid */ bool need_reconnect:1; /* connection reset, uid now invalid */
}; };
/* no more than one of the following three session flags may be set */ /* no more than one of the following three session flags may be set */
...@@ -307,6 +312,44 @@ struct cifsTconInfo { ...@@ -307,6 +312,44 @@ struct cifsTconInfo {
/* BB add field for back pointer to sb struct(s)? */ /* BB add field for back pointer to sb struct(s)? */
}; };
/*
* This is a refcounted and timestamped container for a tcon pointer. The
* container holds a tcon reference. It is considered safe to free one of
* these when the tl_count goes to 0. The tl_time is the time of the last
* "get" on the container.
*/
struct tcon_link {
unsigned long tl_index;
unsigned long tl_flags;
#define TCON_LINK_MASTER 0
#define TCON_LINK_PENDING 1
#define TCON_LINK_IN_TREE 2
unsigned long tl_time;
atomic_t tl_count;
struct cifsTconInfo *tl_tcon;
};
extern struct tcon_link *cifs_sb_tlink(struct cifs_sb_info *cifs_sb);
static inline struct cifsTconInfo *
tlink_tcon(struct tcon_link *tlink)
{
return tlink->tl_tcon;
}
extern void cifs_put_tlink(struct tcon_link *tlink);
static inline struct tcon_link *
cifs_get_tlink(struct tcon_link *tlink)
{
if (tlink && !IS_ERR(tlink))
atomic_inc(&tlink->tl_count);
return tlink;
}
/* This function is always expected to succeed */
extern struct cifsTconInfo *cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb);
/* /*
* This info hangs off the cifsFileInfo structure, pointed to by llist. * This info hangs off the cifsFileInfo structure, pointed to by llist.
* This is used to track byte stream locks on the file * This is used to track byte stream locks on the file
...@@ -345,12 +388,11 @@ struct cifsFileInfo { ...@@ -345,12 +388,11 @@ struct cifsFileInfo {
__u16 netfid; /* file id from remote */ __u16 netfid; /* file id from remote */
/* BB add lock scope info here if needed */ ; /* BB add lock scope info here if needed */ ;
/* lock scope id (0 if none) */ /* lock scope id (0 if none) */
struct file *pfile; /* needed for writepage */ struct dentry *dentry;
struct inode *pInode; /* needed for oplock break */ unsigned int f_flags;
struct vfsmount *mnt; struct tcon_link *tlink;
struct mutex lock_mutex; struct mutex lock_mutex;
struct list_head llist; /* list of byte range locks we have. */ struct list_head llist; /* list of byte range locks we have. */
bool closePend:1; /* file is marked to close */
bool invalidHandle:1; /* file closed via session abend */ bool invalidHandle:1; /* file closed via session abend */
bool oplock_break_cancelled:1; bool oplock_break_cancelled:1;
atomic_t count; /* reference count */ atomic_t count; /* reference count */
...@@ -365,14 +407,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file) ...@@ -365,14 +407,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
atomic_inc(&cifs_file->count); atomic_inc(&cifs_file->count);
} }
/* Release a reference on the file private data */ void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
if (atomic_dec_and_test(&cifs_file->count)) {
iput(cifs_file->pInode);
kfree(cifs_file);
}
}
/* /*
* One of these for each file inode * One of these for each file inode
...@@ -667,7 +702,7 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; ...@@ -667,7 +702,7 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
* the reference counters for the server, smb session, and tcon. Finally, * the reference counters for the server, smb session, and tcon. Finally,
* changes to the tcon->tidStatus should be done while holding this lock. * changes to the tcon->tidStatus should be done while holding this lock.
*/ */
GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; GLOBAL_EXTERN spinlock_t cifs_tcp_ses_lock;
/* /*
* This lock protects the cifs_file->llist and cifs_file->flist * This lock protects the cifs_file->llist and cifs_file->flist
...@@ -676,7 +711,7 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; ...@@ -676,7 +711,7 @@ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
* If cifs_tcp_ses_lock and the lock below are both needed to be held, then * If cifs_tcp_ses_lock and the lock below are both needed to be held, then
* the cifs_tcp_ses_lock must be grabbed first and released last. * the cifs_tcp_ses_lock must be grabbed first and released last.
*/ */
GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; GLOBAL_EXTERN spinlock_t cifs_file_list_lock;
/* Outstanding dir notify requests */ /* Outstanding dir notify requests */
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList; GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
......
...@@ -663,7 +663,6 @@ struct ntlmv2_resp { ...@@ -663,7 +663,6 @@ struct ntlmv2_resp {
__le64 time; __le64 time;
__u64 client_chal; /* random */ __u64 client_chal; /* random */
__u32 reserved2; __u32 reserved2;
struct ntlmssp2_name names[2];
/* array of name entries could follow ending in minimum 4 byte struct */ /* array of name entries could follow ending in minimum 4 byte struct */
} __attribute__((packed)); } __attribute__((packed));
......
...@@ -78,9 +78,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length); ...@@ -78,9 +78,9 @@ extern int checkSMB(struct smb_hdr *smb, __u16 mid, unsigned int length);
extern bool is_valid_oplock_break(struct smb_hdr *smb, extern bool is_valid_oplock_break(struct smb_hdr *smb,
struct TCP_Server_Info *); struct TCP_Server_Info *);
extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof); extern bool is_size_safe_to_change(struct cifsInodeInfo *, __u64 eof);
extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *); extern struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *, bool);
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *); extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
#endif #endif
extern unsigned int smbCalcSize(struct smb_hdr *ptr); extern unsigned int smbCalcSize(struct smb_hdr *ptr);
extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr); extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
...@@ -105,12 +105,12 @@ extern u64 cifs_UnixTimeToNT(struct timespec); ...@@ -105,12 +105,12 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
int offset); int offset);
extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode, extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle,
__u16 fileHandle, struct file *file, struct file *file, struct tcon_link *tlink,
struct vfsmount *mnt, unsigned int oflags); __u32 oplock);
extern int cifs_posix_open(char *full_path, struct inode **pinode, extern int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, struct super_block *sb,
int mode, int oflags, int mode, unsigned int f_flags,
__u32 *poplock, __u16 *pnetfid, int xid); __u32 *poplock, __u16 *pnetfid, int xid);
void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr); void cifs_fill_uniqueid(struct super_block *sb, struct cifs_fattr *fattr);
extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, extern void cifs_unix_basic_to_fattr(struct cifs_fattr *fattr,
...@@ -362,12 +362,12 @@ extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *); ...@@ -362,12 +362,12 @@ extern int cifs_sign_smb(struct smb_hdr *, struct TCP_Server_Info *, __u32 *);
extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *, extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
__u32 *); __u32 *);
extern int cifs_verify_signature(struct smb_hdr *, extern int cifs_verify_signature(struct smb_hdr *,
const struct mac_key *mac_key, const struct session_key *session_key,
__u32 expected_sequence_number); __u32 expected_sequence_number);
extern int cifs_calculate_mac_key(struct mac_key *key, const char *rn, extern int cifs_calculate_session_key(struct session_key *key, const char *rn,
const char *pass); const char *pass);
extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *); extern void CalcNTLMv2_response(const struct cifsSesInfo *, char *);
extern void setup_ntlmv2_rsp(struct cifsSesInfo *, char *, extern int setup_ntlmv2_rsp(struct cifsSesInfo *, char *,
const struct nls_table *); const struct nls_table *);
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
extern void calc_lanman_hash(const char *password, const char *cryptkey, extern void calc_lanman_hash(const char *password, const char *cryptkey,
...@@ -408,4 +408,8 @@ extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon, ...@@ -408,4 +408,8 @@ extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon, extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
const int netfid, __u64 *pExtAttrBits, __u64 *pMask); const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb); extern void cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb);
extern bool CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr);
extern int CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, int xid);
#endif /* _CIFSPROTO_H */ #endif /* _CIFSPROTO_H */
...@@ -91,13 +91,13 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon) ...@@ -91,13 +91,13 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
struct list_head *tmp1; struct list_head *tmp1;
/* list all files open on tree connection and mark them invalid */ /* list all files open on tree connection and mark them invalid */
write_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
list_for_each_safe(tmp, tmp1, &pTcon->openFileList) { list_for_each_safe(tmp, tmp1, &pTcon->openFileList) {
open_file = list_entry(tmp, struct cifsFileInfo, tlist); open_file = list_entry(tmp, struct cifsFileInfo, tlist);
open_file->invalidHandle = true; open_file->invalidHandle = true;
open_file->oplock_break_cancelled = true; open_file->oplock_break_cancelled = true;
} }
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
/* BB Add call to invalidate_inodes(sb) for all superblocks mounted /* BB Add call to invalidate_inodes(sb) for all superblocks mounted
to this tcon */ to this tcon */
} }
...@@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
if (rsp->EncryptionKeyLength == if (rsp->EncryptionKeyLength ==
cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) { cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
memcpy(server->cryptKey, rsp->EncryptionKey, memcpy(ses->cryptKey, rsp->EncryptionKey,
CIFS_CRYPTO_KEY_SIZE); CIFS_CRYPTO_KEY_SIZE);
} else if (server->secMode & SECMODE_PW_ENCRYPT) { } else if (server->secMode & SECMODE_PW_ENCRYPT) {
rc = -EIO; /* need cryptkey unless plain text */ rc = -EIO; /* need cryptkey unless plain text */
...@@ -574,7 +574,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -574,7 +574,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone); server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
server->timeAdj *= 60; server->timeAdj *= 60;
if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) { if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
memcpy(server->cryptKey, pSMBr->u.EncryptionKey, memcpy(ses->cryptKey, pSMBr->u.EncryptionKey,
CIFS_CRYPTO_KEY_SIZE); CIFS_CRYPTO_KEY_SIZE);
} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC) } else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
&& (pSMBr->EncryptionKeyLength == 0)) { && (pSMBr->EncryptionKeyLength == 0)) {
...@@ -593,9 +593,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -593,9 +593,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
rc = -EIO; rc = -EIO;
goto neg_err_exit; goto neg_err_exit;
} }
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (server->srv_count > 1) { if (server->srv_count > 1) {
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
if (memcmp(server->server_GUID, if (memcmp(server->server_GUID,
pSMBr->u.extended_response. pSMBr->u.extended_response.
GUID, 16) != 0) { GUID, 16) != 0) {
...@@ -605,7 +605,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -605,7 +605,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
16); 16);
} }
} else { } else {
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
memcpy(server->server_GUID, memcpy(server->server_GUID,
pSMBr->u.extended_response.GUID, 16); pSMBr->u.extended_response.GUID, 16);
} }
...@@ -620,12 +620,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses) ...@@ -620,12 +620,14 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
rc = 0; rc = 0;
else else
rc = -EINVAL; rc = -EINVAL;
if (server->secType == Kerberos) {
if (server->sec_kerberos || server->sec_mskerberos) if (!server->sec_kerberos &&
server->secType = Kerberos; !server->sec_mskerberos)
else if (server->sec_ntlmssp) rc = -EOPNOTSUPP;
server->secType = RawNTLMSSP; } else if (server->secType == RawNTLMSSP) {
else if (!server->sec_ntlmssp)
rc = -EOPNOTSUPP;
} else
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
} }
} else } else
......
/*
* fs/cifs/cn_cifs.h
*
* Copyright (c) International Business Machines Corp., 2002
* Author(s): Steve French (sfrench@us.ibm.com)
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
* the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef _CN_CIFS_H
#define _CN_CIFS_H
#ifdef CONFIG_CIFS_UPCALL
#include <linux/types.h>
#include <linux/connector.h>
struct cifs_upcall {
char signature[4]; /* CIFS */
enum command {
CIFS_GET_IP = 0x00000001, /* get ip address for hostname */
CIFS_GET_SECBLOB = 0x00000002, /* get SPNEGO wrapped blob */
} command;
/* union cifs upcall data follows */
};
#endif /* CIFS_UPCALL */
#endif /* _CN_CIFS_H */
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
#include "ntlmssp.h" #include "ntlmssp.h"
#include "nterr.h" #include "nterr.h"
#include "rfc1002pdu.h" #include "rfc1002pdu.h"
#include "cn_cifs.h"
#include "fscache.h" #include "fscache.h"
#define CIFS_PORT 445 #define CIFS_PORT 445
...@@ -100,16 +99,24 @@ struct smb_vol { ...@@ -100,16 +99,24 @@ struct smb_vol {
bool noautotune:1; bool noautotune:1;
bool nostrictsync:1; /* do not force expensive SMBflush on every sync */ bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
bool fsc:1; /* enable fscache */ bool fsc:1; /* enable fscache */
bool mfsymlinks:1; /* use Minshall+French Symlinks */
bool multiuser:1;
unsigned int rsize; unsigned int rsize;
unsigned int wsize; unsigned int wsize;
bool sockopt_tcp_nodelay:1; bool sockopt_tcp_nodelay:1;
unsigned short int port; unsigned short int port;
char *prepath; char *prepath;
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
struct nls_table *local_nls; struct nls_table *local_nls;
}; };
/* FIXME: should these be tunable? */
#define TLINK_ERROR_EXPIRE (1 * HZ)
#define TLINK_IDLE_EXPIRE (600 * HZ)
static int ipv4_connect(struct TCP_Server_Info *server); static int ipv4_connect(struct TCP_Server_Info *server);
static int ipv6_connect(struct TCP_Server_Info *server); static int ipv6_connect(struct TCP_Server_Info *server);
static void cifs_prune_tlinks(struct work_struct *work);
/* /*
* cifs tcp session reconnection * cifs tcp session reconnection
...@@ -143,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -143,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
/* before reconnecting the tcp session, mark the smb session (uid) /* before reconnecting the tcp session, mark the smb session (uid)
and the tid bad so they are not used until reconnected */ and the tid bad so they are not used until reconnected */
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &server->smb_ses_list) { list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
ses->need_reconnect = true; ses->need_reconnect = true;
...@@ -153,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -153,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
tcon->need_reconnect = true; tcon->need_reconnect = true;
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
/* do not want to be sending data on a socket we are freeing */ /* do not want to be sending data on a socket we are freeing */
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
if (server->ssocket) { if (server->ssocket) {
...@@ -166,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -166,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
sock_release(server->ssocket); sock_release(server->ssocket);
server->ssocket = NULL; server->ssocket = NULL;
} }
server->sequence_number = 0;
server->session_estab = false;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
list_for_each(tmp, &server->pending_mid_q) { list_for_each(tmp, &server->pending_mid_q) {
...@@ -198,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server) ...@@ -198,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (server->tcpStatus != CifsExiting) if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsGood; server->tcpStatus = CifsGood;
server->sequence_number = 0;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
/* atomic_set(&server->inFlight,0);*/ /* atomic_set(&server->inFlight,0);*/
wake_up(&server->response_q); wake_up(&server->response_q);
...@@ -629,9 +637,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -629,9 +637,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
} /* end while !EXITING */ } /* end while !EXITING */
/* take it off the list, if it's not already */ /* take it off the list, if it's not already */
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_del_init(&server->tcp_ses_list); list_del_init(&server->tcp_ses_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
...@@ -669,7 +677,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -669,7 +677,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
* BB: we shouldn't have to do any of this. It shouldn't be * BB: we shouldn't have to do any of this. It shouldn't be
* possible to exit from the thread with active SMB sessions * possible to exit from the thread with active SMB sessions
*/ */
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (list_empty(&server->pending_mid_q)) { if (list_empty(&server->pending_mid_q)) {
/* loop through server session structures attached to this and /* loop through server session structures attached to this and
mark them dead */ mark them dead */
...@@ -679,7 +687,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -679,7 +687,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
ses->status = CifsExiting; ses->status = CifsExiting;
ses->server = NULL; ses->server = NULL;
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
} else { } else {
/* although we can not zero the server struct pointer yet, /* although we can not zero the server struct pointer yet,
since there are active requests which may depnd on them, since there are active requests which may depnd on them,
...@@ -702,7 +710,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -702,7 +710,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
} }
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
/* 1/8th of sec is more than enough time for them to exit */ /* 1/8th of sec is more than enough time for them to exit */
msleep(125); msleep(125);
} }
...@@ -725,12 +733,12 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server) ...@@ -725,12 +733,12 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
if a crazy root user tried to kill cifsd if a crazy root user tried to kill cifsd
kernel thread explicitly this might happen) */ kernel thread explicitly this might happen) */
/* BB: This shouldn't be necessary, see above */ /* BB: This shouldn't be necessary, see above */
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &server->smb_ses_list) { list_for_each(tmp, &server->smb_ses_list) {
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
ses->server = NULL; ses->server = NULL;
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
kfree(server->hostname); kfree(server->hostname);
task_to_wake = xchg(&server->tsk, NULL); task_to_wake = xchg(&server->tsk, NULL);
...@@ -1046,6 +1054,22 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1046,6 +1054,22 @@ cifs_parse_mount_options(char *options, const char *devname,
"long\n"); "long\n");
return 1; return 1;
} }
} else if (strnicmp(data, "srcaddr", 7) == 0) {
vol->srcaddr.ss_family = AF_UNSPEC;
if (!value || !*value) {
printk(KERN_WARNING "CIFS: srcaddr value"
" not specified.\n");
return 1; /* needs_arg; */
}
i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
value, strlen(value));
if (i < 0) {
printk(KERN_WARNING "CIFS: Could not parse"
" srcaddr: %s\n",
value);
return 1;
}
} else if (strnicmp(data, "prefixpath", 10) == 0) { } else if (strnicmp(data, "prefixpath", 10) == 0) {
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING printk(KERN_WARNING
...@@ -1325,6 +1349,10 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1325,6 +1349,10 @@ cifs_parse_mount_options(char *options, const char *devname,
"/proc/fs/cifs/LookupCacheEnabled to 0\n"); "/proc/fs/cifs/LookupCacheEnabled to 0\n");
} else if (strnicmp(data, "fsc", 3) == 0) { } else if (strnicmp(data, "fsc", 3) == 0) {
vol->fsc = true; vol->fsc = true;
} else if (strnicmp(data, "mfsymlinks", 10) == 0) {
vol->mfsymlinks = true;
} else if (strnicmp(data, "multiuser", 8) == 0) {
vol->multiuser = true;
} else } else
printk(KERN_WARNING "CIFS: Unknown mount option %s\n", printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
data); data);
...@@ -1356,6 +1384,13 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1356,6 +1384,13 @@ cifs_parse_mount_options(char *options, const char *devname,
return 1; return 1;
} }
} }
if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) {
cERROR(1, "Multiuser mounts currently require krb5 "
"authentication!");
return 1;
}
if (vol->UNCip == NULL) if (vol->UNCip == NULL)
vol->UNCip = &vol->UNC[2]; vol->UNCip = &vol->UNC[2];
...@@ -1374,8 +1409,36 @@ cifs_parse_mount_options(char *options, const char *devname, ...@@ -1374,8 +1409,36 @@ cifs_parse_mount_options(char *options, const char *devname,
return 0; return 0;
} }
/** Returns true if srcaddr isn't specified and rhs isn't
* specified, or if srcaddr is specified and
* matches the IP address of the rhs argument.
*/
static bool
srcip_matches(struct sockaddr *srcaddr, struct sockaddr *rhs)
{
switch (srcaddr->sa_family) {
case AF_UNSPEC:
return (rhs->sa_family == AF_UNSPEC);
case AF_INET: {
struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
}
case AF_INET6: {
struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)&rhs;
return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);
}
default:
WARN_ON(1);
return false; /* don't expect to be here */
}
}
static bool static bool
match_address(struct TCP_Server_Info *server, struct sockaddr *addr) match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
struct sockaddr *srcaddr)
{ {
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr; struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr; struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
...@@ -1402,6 +1465,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr) ...@@ -1402,6 +1465,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
break; break;
} }
if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
return false;
return true; return true;
} }
...@@ -1458,29 +1524,21 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol) ...@@ -1458,29 +1524,21 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
{ {
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
/* if (!match_address(server, addr,
* the demux thread can exit on its own while still in CifsNew (struct sockaddr *)&vol->srcaddr))
* so don't accept any sockets in that state. Since the
* tcpStatus never changes back to CifsNew it's safe to check
* for this without a lock.
*/
if (server->tcpStatus == CifsNew)
continue;
if (!match_address(server, addr))
continue; continue;
if (!match_security(server, vol)) if (!match_security(server, vol))
continue; continue;
++server->srv_count; ++server->srv_count;
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cFYI(1, "Existing tcp session with server found"); cFYI(1, "Existing tcp session with server found");
return server; return server;
} }
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return NULL; return NULL;
} }
...@@ -1489,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server) ...@@ -1489,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
{ {
struct task_struct *task; struct task_struct *task;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (--server->srv_count > 0) { if (--server->srv_count > 0) {
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return; return;
} }
list_del_init(&server->tcp_ses_list); list_del_init(&server->tcp_ses_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
...@@ -1574,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -1574,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
memcpy(tcp_ses->server_RFC1001_name, memcpy(tcp_ses->server_RFC1001_name,
volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL); volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
tcp_ses->session_estab = false;
tcp_ses->sequence_number = 0; tcp_ses->sequence_number = 0;
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list); INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
INIT_LIST_HEAD(&tcp_ses->smb_ses_list); INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
...@@ -1584,6 +1643,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -1584,6 +1643,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
* no need to spinlock this init of tcpStatus or srv_count * no need to spinlock this init of tcpStatus or srv_count
*/ */
tcp_ses->tcpStatus = CifsNew; tcp_ses->tcpStatus = CifsNew;
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
sizeof(tcp_ses->srcaddr));
++tcp_ses->srv_count; ++tcp_ses->srv_count;
if (addr.ss_family == AF_INET6) { if (addr.ss_family == AF_INET6) {
...@@ -1618,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -1618,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
} }
/* thread spawned, put it on the list */ /* thread spawned, put it on the list */
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list); list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_fscache_get_client_cookie(tcp_ses); cifs_fscache_get_client_cookie(tcp_ses);
...@@ -1642,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) ...@@ -1642,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
{ {
struct cifsSesInfo *ses; struct cifsSesInfo *ses;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
switch (server->secType) { switch (server->secType) {
case Kerberos: case Kerberos:
...@@ -1662,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol) ...@@ -1662,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
continue; continue;
} }
++ses->ses_count; ++ses->ses_count;
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return ses; return ses;
} }
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return NULL; return NULL;
} }
...@@ -1676,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses) ...@@ -1676,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
struct TCP_Server_Info *server = ses->server; struct TCP_Server_Info *server = ses->server;
cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count); cFYI(1, "%s: ses_count=%d\n", __func__, ses->ses_count);
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (--ses->ses_count > 0) { if (--ses->ses_count > 0) {
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return; return;
} }
list_del_init(&ses->smb_ses_list); list_del_init(&ses->smb_ses_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
if (ses->status == CifsGood) { if (ses->status == CifsGood) {
xid = GetXid(); xid = GetXid();
...@@ -1740,6 +1801,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ...@@ -1740,6 +1801,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
if (ses == NULL) if (ses == NULL)
goto get_ses_fail; goto get_ses_fail;
ses->tilen = 0;
ses->tiblob = NULL;
/* new SMB session uses our server ref */ /* new SMB session uses our server ref */
ses->server = server; ses->server = server;
if (server->addr.sockAddr6.sin6_family == AF_INET6) if (server->addr.sockAddr6.sin6_family == AF_INET6)
...@@ -1778,9 +1841,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info) ...@@ -1778,9 +1841,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
goto get_ses_fail; goto get_ses_fail;
/* success, put it on the list */ /* success, put it on the list */
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_add(&ses->smb_ses_list, &server->smb_ses_list); list_add(&ses->smb_ses_list, &server->smb_ses_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
FreeXid(xid); FreeXid(xid);
return ses; return ses;
...@@ -1797,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) ...@@ -1797,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
struct list_head *tmp; struct list_head *tmp;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &ses->tcon_list) { list_for_each(tmp, &ses->tcon_list) {
tcon = list_entry(tmp, struct cifsTconInfo, tcon_list); tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
if (tcon->tidStatus == CifsExiting) if (tcon->tidStatus == CifsExiting)
...@@ -1806,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc) ...@@ -1806,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
continue; continue;
++tcon->tc_count; ++tcon->tc_count;
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return tcon; return tcon;
} }
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return NULL; return NULL;
} }
...@@ -1820,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon) ...@@ -1820,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
struct cifsSesInfo *ses = tcon->ses; struct cifsSesInfo *ses = tcon->ses;
cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count); cFYI(1, "%s: tc_count=%d\n", __func__, tcon->tc_count);
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (--tcon->tc_count > 0) { if (--tcon->tc_count > 0) {
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return; return;
} }
list_del_init(&tcon->tcon_list); list_del_init(&tcon->tcon_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
xid = GetXid(); xid = GetXid();
CIFSSMBTDis(xid, tcon); CIFSSMBTDis(xid, tcon);
...@@ -1900,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) ...@@ -1900,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)
tcon->nocase = volume_info->nocase; tcon->nocase = volume_info->nocase;
tcon->local_lease = volume_info->local_lease; tcon->local_lease = volume_info->local_lease;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_add(&tcon->tcon_list, &ses->tcon_list); list_add(&tcon->tcon_list, &ses->tcon_list);
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_fscache_get_super_cookie(tcon); cifs_fscache_get_super_cookie(tcon);
...@@ -1913,6 +1976,23 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info) ...@@ -1913,6 +1976,23 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)
return ERR_PTR(rc); return ERR_PTR(rc);
} }
void
cifs_put_tlink(struct tcon_link *tlink)
{
if (!tlink || IS_ERR(tlink))
return;
if (!atomic_dec_and_test(&tlink->tl_count) ||
test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) {
tlink->tl_time = jiffies;
return;
}
if (!IS_ERR(tlink_tcon(tlink)))
cifs_put_tcon(tlink_tcon(tlink));
kfree(tlink);
return;
}
int int
get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
...@@ -1997,6 +2077,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length) ...@@ -1997,6 +2077,33 @@ static void rfc1002mangle(char *target, char *source, unsigned int length)
} }
static int
bind_socket(struct TCP_Server_Info *server)
{
int rc = 0;
if (server->srcaddr.ss_family != AF_UNSPEC) {
/* Bind to the specified local IP address */
struct socket *socket = server->ssocket;
rc = socket->ops->bind(socket,
(struct sockaddr *) &server->srcaddr,
sizeof(server->srcaddr));
if (rc < 0) {
struct sockaddr_in *saddr4;
struct sockaddr_in6 *saddr6;
saddr4 = (struct sockaddr_in *)&server->srcaddr;
saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
if (saddr6->sin6_family == AF_INET6)
cERROR(1, "cifs: "
"Failed to bind to: %pI6c, error: %d\n",
&saddr6->sin6_addr, rc);
else
cERROR(1, "cifs: "
"Failed to bind to: %pI4, error: %d\n",
&saddr4->sin_addr.s_addr, rc);
}
}
return rc;
}
static int static int
ipv4_connect(struct TCP_Server_Info *server) ipv4_connect(struct TCP_Server_Info *server)
...@@ -2022,6 +2129,10 @@ ipv4_connect(struct TCP_Server_Info *server) ...@@ -2022,6 +2129,10 @@ ipv4_connect(struct TCP_Server_Info *server)
cifs_reclassify_socket4(socket); cifs_reclassify_socket4(socket);
} }
rc = bind_socket(server);
if (rc < 0)
return rc;
/* user overrode default port */ /* user overrode default port */
if (server->addr.sockAddr.sin_port) { if (server->addr.sockAddr.sin_port) {
rc = socket->ops->connect(socket, (struct sockaddr *) rc = socket->ops->connect(socket, (struct sockaddr *)
...@@ -2184,6 +2295,10 @@ ipv6_connect(struct TCP_Server_Info *server) ...@@ -2184,6 +2295,10 @@ ipv6_connect(struct TCP_Server_Info *server)
cifs_reclassify_socket6(socket); cifs_reclassify_socket6(socket);
} }
rc = bind_socket(server);
if (rc < 0)
return rc;
/* user overrode default port */ /* user overrode default port */
if (server->addr.sockAddr6.sin6_port) { if (server->addr.sockAddr6.sin6_port) {
rc = socket->ops->connect(socket, rc = socket->ops->connect(socket,
...@@ -2383,6 +2498,8 @@ convert_delimiter(char *path, char delim) ...@@ -2383,6 +2498,8 @@ convert_delimiter(char *path, char delim)
static void setup_cifs_sb(struct smb_vol *pvolume_info, static void setup_cifs_sb(struct smb_vol *pvolume_info,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
if (pvolume_info->rsize > CIFSMaxBufSize) { if (pvolume_info->rsize > CIFSMaxBufSize) {
cERROR(1, "rsize %d too large, using MaxBufSize", cERROR(1, "rsize %d too large, using MaxBufSize",
pvolume_info->rsize); pvolume_info->rsize);
...@@ -2462,10 +2579,21 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info, ...@@ -2462,10 +2579,21 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
if (pvolume_info->fsc) if (pvolume_info->fsc)
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE;
if (pvolume_info->multiuser)
cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER |
CIFS_MOUNT_NO_PERM);
if (pvolume_info->direct_io) { if (pvolume_info->direct_io) {
cFYI(1, "mounting share using direct i/o"); cFYI(1, "mounting share using direct i/o");
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
} }
if (pvolume_info->mfsymlinks) {
if (pvolume_info->sfu_emul) {
cERROR(1, "mount option mfsymlinks ignored if sfu "
"mount option is used");
} else {
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
}
}
if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm)) if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
cERROR(1, "mount option dynperm ignored if cifsacl " cERROR(1, "mount option dynperm ignored if cifsacl "
...@@ -2552,6 +2680,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2552,6 +2680,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
struct TCP_Server_Info *srvTcp; struct TCP_Server_Info *srvTcp;
char *full_path; char *full_path;
char *mount_data = mount_data_global; char *mount_data = mount_data_global;
struct tcon_link *tlink;
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
struct dfs_info3_param *referrals = NULL; struct dfs_info3_param *referrals = NULL;
unsigned int num_referrals = 0; unsigned int num_referrals = 0;
...@@ -2563,6 +2692,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2563,6 +2692,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
pSesInfo = NULL; pSesInfo = NULL;
srvTcp = NULL; srvTcp = NULL;
full_path = NULL; full_path = NULL;
tlink = NULL;
xid = GetXid(); xid = GetXid();
...@@ -2638,8 +2768,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2638,8 +2768,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
goto remote_path_check; goto remote_path_check;
} }
cifs_sb->tcon = tcon;
/* do not care if following two calls succeed - informational */ /* do not care if following two calls succeed - informational */
if (!tcon->ipc) { if (!tcon->ipc) {
CIFSSMBQFSDeviceInfo(xid, tcon); CIFSSMBQFSDeviceInfo(xid, tcon);
...@@ -2748,6 +2876,38 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, ...@@ -2748,6 +2876,38 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
#endif #endif
} }
if (rc)
goto mount_fail_check;
/* now, hang the tcon off of the superblock */
tlink = kzalloc(sizeof *tlink, GFP_KERNEL);
if (tlink == NULL) {
rc = -ENOMEM;
goto mount_fail_check;
}
tlink->tl_index = pSesInfo->linux_uid;
tlink->tl_tcon = tcon;
tlink->tl_time = jiffies;
set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
rc = radix_tree_preload(GFP_KERNEL);
if (rc == -ENOMEM) {
kfree(tlink);
goto mount_fail_check;
}
spin_lock(&cifs_sb->tlink_tree_lock);
radix_tree_insert(&cifs_sb->tlink_tree, pSesInfo->linux_uid, tlink);
radix_tree_tag_set(&cifs_sb->tlink_tree, pSesInfo->linux_uid,
CIFS_TLINK_MASTER_TAG);
spin_unlock(&cifs_sb->tlink_tree_lock);
radix_tree_preload_end();
queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE);
mount_fail_check: mount_fail_check:
/* on error free sesinfo and tcon struct if needed */ /* on error free sesinfo and tcon struct if needed */
if (rc) { if (rc) {
...@@ -2825,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, ...@@ -2825,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
#ifdef CONFIG_CIFS_WEAK_PW_HASH #ifdef CONFIG_CIFS_WEAK_PW_HASH
if ((global_secflags & CIFSSEC_MAY_LANMAN) && if ((global_secflags & CIFSSEC_MAY_LANMAN) &&
(ses->server->secType == LANMAN)) (ses->server->secType == LANMAN))
calc_lanman_hash(tcon->password, ses->server->cryptKey, calc_lanman_hash(tcon->password, ses->cryptKey,
ses->server->secMode & ses->server->secMode &
SECMODE_PW_ENCRYPT ? true : false, SECMODE_PW_ENCRYPT ? true : false,
bcc_ptr); bcc_ptr);
else else
#endif /* CIFS_WEAK_PW_HASH */ #endif /* CIFS_WEAK_PW_HASH */
SMBNTencrypt(tcon->password, ses->server->cryptKey, SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr);
bcc_ptr);
bcc_ptr += CIFS_SESS_KEY_SIZE; bcc_ptr += CIFS_SESS_KEY_SIZE;
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
...@@ -2934,19 +3093,39 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses, ...@@ -2934,19 +3093,39 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
int int
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
{ {
int rc = 0; int i, ret;
char *tmp; char *tmp;
struct tcon_link *tlink[8];
unsigned long index = 0;
cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
do {
spin_lock(&cifs_sb->tlink_tree_lock);
ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree,
(void **)tlink, index,
ARRAY_SIZE(tlink));
/* increment index for next pass */
if (ret > 0)
index = tlink[ret - 1]->tl_index + 1;
for (i = 0; i < ret; i++) {
cifs_get_tlink(tlink[i]);
clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags);
radix_tree_delete(&cifs_sb->tlink_tree,
tlink[i]->tl_index);
}
spin_unlock(&cifs_sb->tlink_tree_lock);
for (i = 0; i < ret; i++)
cifs_put_tlink(tlink[i]);
} while (ret != 0);
if (cifs_sb->tcon)
cifs_put_tcon(cifs_sb->tcon);
cifs_sb->tcon = NULL;
tmp = cifs_sb->prepath; tmp = cifs_sb->prepath;
cifs_sb->prepathlen = 0; cifs_sb->prepathlen = 0;
cifs_sb->prepath = NULL; cifs_sb->prepath = NULL;
kfree(tmp); kfree(tmp);
return rc; return 0;
} }
int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses) int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses)
...@@ -2997,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, ...@@ -2997,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses,
if (rc) { if (rc) {
cERROR(1, "Send error in SessSetup = %d", rc); cERROR(1, "Send error in SessSetup = %d", rc);
} else { } else {
mutex_lock(&ses->server->srv_mutex);
if (!server->session_estab) {
memcpy(&server->session_key.data,
&ses->auth_key.data, ses->auth_key.len);
server->session_key.len = ses->auth_key.len;
ses->server->session_estab = true;
}
mutex_unlock(&server->srv_mutex);
cFYI(1, "CIFS Session Established successfully"); cFYI(1, "CIFS Session Established successfully");
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
ses->status = CifsGood; ses->status = CifsGood;
...@@ -3007,3 +3195,237 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses, ...@@ -3007,3 +3195,237 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses,
return rc; return rc;
} }
static struct cifsTconInfo *
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, uid_t fsuid)
{
struct cifsTconInfo *master_tcon = cifs_sb_master_tcon(cifs_sb);
struct cifsSesInfo *ses;
struct cifsTconInfo *tcon = NULL;
struct smb_vol *vol_info;
char username[MAX_USERNAME_SIZE + 1];
vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL);
if (vol_info == NULL) {
tcon = ERR_PTR(-ENOMEM);
goto out;
}
snprintf(username, MAX_USERNAME_SIZE, "krb50x%x", fsuid);
vol_info->username = username;
vol_info->local_nls = cifs_sb->local_nls;
vol_info->linux_uid = fsuid;
vol_info->cred_uid = fsuid;
vol_info->UNC = master_tcon->treeName;
vol_info->retry = master_tcon->retry;
vol_info->nocase = master_tcon->nocase;
vol_info->local_lease = master_tcon->local_lease;
vol_info->no_linux_ext = !master_tcon->unix_ext;
/* FIXME: allow for other secFlg settings */
vol_info->secFlg = CIFSSEC_MUST_KRB5;
/* get a reference for the same TCP session */
spin_lock(&cifs_tcp_ses_lock);
++master_tcon->ses->server->srv_count;
spin_unlock(&cifs_tcp_ses_lock);
ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
if (IS_ERR(ses)) {
tcon = (struct cifsTconInfo *)ses;
cifs_put_tcp_session(master_tcon->ses->server);
goto out;
}
tcon = cifs_get_tcon(ses, vol_info);
if (IS_ERR(tcon)) {
cifs_put_smb_ses(ses);
goto out;
}
if (ses->capabilities & CAP_UNIX)
reset_cifs_unix_caps(0, tcon, NULL, vol_info);
out:
kfree(vol_info);
return tcon;
}
static struct tcon_link *
cifs_sb_master_tlink(struct cifs_sb_info *cifs_sb)
{
struct tcon_link *tlink;
unsigned int ret;
spin_lock(&cifs_sb->tlink_tree_lock);
ret = radix_tree_gang_lookup_tag(&cifs_sb->tlink_tree, (void **)&tlink,
0, 1, CIFS_TLINK_MASTER_TAG);
spin_unlock(&cifs_sb->tlink_tree_lock);
/* the master tcon should always be present */
if (ret == 0)
BUG();
return tlink;
}
struct cifsTconInfo *
cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
{
return tlink_tcon(cifs_sb_master_tlink(cifs_sb));
}
static int
cifs_sb_tcon_pending_wait(void *unused)
{
schedule();
return signal_pending(current) ? -ERESTARTSYS : 0;
}
/*
* Find or construct an appropriate tcon given a cifs_sb and the fsuid of the
* current task.
*
* If the superblock doesn't refer to a multiuser mount, then just return
* the master tcon for the mount.
*
* First, search the radix tree for an existing tcon for this fsuid. If one
* exists, then check to see if it's pending construction. If it is then wait
* for construction to complete. Once it's no longer pending, check to see if
* it failed and either return an error or retry construction, depending on
* the timeout.
*
* If one doesn't exist then insert a new tcon_link struct into the tree and
* try to construct a new one.
*/
struct tcon_link *
cifs_sb_tlink(struct cifs_sb_info *cifs_sb)
{
int ret;
unsigned long fsuid = (unsigned long) current_fsuid();
struct tcon_link *tlink, *newtlink;
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
spin_lock(&cifs_sb->tlink_tree_lock);
tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid);
if (tlink)
cifs_get_tlink(tlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
if (tlink == NULL) {
newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
if (newtlink == NULL)
return ERR_PTR(-ENOMEM);
newtlink->tl_index = fsuid;
newtlink->tl_tcon = ERR_PTR(-EACCES);
set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
cifs_get_tlink(newtlink);
ret = radix_tree_preload(GFP_KERNEL);
if (ret != 0) {
kfree(newtlink);
return ERR_PTR(ret);
}
spin_lock(&cifs_sb->tlink_tree_lock);
/* was one inserted after previous search? */
tlink = radix_tree_lookup(&cifs_sb->tlink_tree, fsuid);
if (tlink) {
cifs_get_tlink(tlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
radix_tree_preload_end();
kfree(newtlink);
goto wait_for_construction;
}
ret = radix_tree_insert(&cifs_sb->tlink_tree, fsuid, newtlink);
spin_unlock(&cifs_sb->tlink_tree_lock);
radix_tree_preload_end();
if (ret) {
kfree(newtlink);
return ERR_PTR(ret);
}
tlink = newtlink;
} else {
wait_for_construction:
ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
cifs_sb_tcon_pending_wait,
TASK_INTERRUPTIBLE);
if (ret) {
cifs_put_tlink(tlink);
return ERR_PTR(ret);
}
/* if it's good, return it */
if (!IS_ERR(tlink->tl_tcon))
return tlink;
/* return error if we tried this already recently */
if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
cifs_put_tlink(tlink);
return ERR_PTR(-EACCES);
}
if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
goto wait_for_construction;
}
tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid);
clear_bit(TCON_LINK_PENDING, &tlink->tl_flags);
wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
if (IS_ERR(tlink->tl_tcon)) {
cifs_put_tlink(tlink);
return ERR_PTR(-EACCES);
}
return tlink;
}
/*
* periodic workqueue job that scans tcon_tree for a superblock and closes
* out tcons.
*/
static void
cifs_prune_tlinks(struct work_struct *work)
{
struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info,
prune_tlinks.work);
struct tcon_link *tlink[8];
unsigned long now = jiffies;
unsigned long index = 0;
int i, ret;
do {
spin_lock(&cifs_sb->tlink_tree_lock);
ret = radix_tree_gang_lookup(&cifs_sb->tlink_tree,
(void **)tlink, index,
ARRAY_SIZE(tlink));
/* increment index for next pass */
if (ret > 0)
index = tlink[ret - 1]->tl_index + 1;
for (i = 0; i < ret; i++) {
if (test_bit(TCON_LINK_MASTER, &tlink[i]->tl_flags) ||
atomic_read(&tlink[i]->tl_count) != 0 ||
time_after(tlink[i]->tl_time + TLINK_IDLE_EXPIRE,
now)) {
tlink[i] = NULL;
continue;
}
cifs_get_tlink(tlink[i]);
clear_bit(TCON_LINK_IN_TREE, &tlink[i]->tl_flags);
radix_tree_delete(&cifs_sb->tlink_tree,
tlink[i]->tl_index);
}
spin_unlock(&cifs_sb->tlink_tree_lock);
for (i = 0; i < ret; i++) {
if (tlink[i] != NULL)
cifs_put_tlink(tlink[i]);
}
} while (ret != 0);
queue_delayed_work(system_nrt_wq, &cifs_sb->prune_tlinks,
TLINK_IDLE_EXPIRE);
}
...@@ -54,18 +54,18 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -54,18 +54,18 @@ build_path_from_dentry(struct dentry *direntry)
int dfsplen; int dfsplen;
char *full_path; char *full_path;
char dirsep; char dirsep;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(direntry->d_sb);
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
if (direntry == NULL) if (direntry == NULL)
return NULL; /* not much we can do if dentry is freed and return NULL; /* not much we can do if dentry is freed and
we need to reopen the file after it was closed implicitly we need to reopen the file after it was closed implicitly
when the server crashed */ when the server crashed */
cifs_sb = CIFS_SB(direntry->d_sb);
dirsep = CIFS_DIR_SEP(cifs_sb); dirsep = CIFS_DIR_SEP(cifs_sb);
pplen = cifs_sb->prepathlen; pplen = cifs_sb->prepathlen;
if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else else
dfsplen = 0; dfsplen = 0;
cifs_bp_rename_retry: cifs_bp_rename_retry:
...@@ -117,7 +117,7 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -117,7 +117,7 @@ build_path_from_dentry(struct dentry *direntry)
/* BB test paths to Windows with '/' in the midst of prepath */ /* BB test paths to Windows with '/' in the midst of prepath */
if (dfsplen) { if (dfsplen) {
strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); strncpy(full_path, tcon->treeName, dfsplen);
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) {
int i; int i;
for (i = 0; i < dfsplen; i++) { for (i = 0; i < dfsplen; i++) {
...@@ -130,135 +130,6 @@ build_path_from_dentry(struct dentry *direntry) ...@@ -130,135 +130,6 @@ build_path_from_dentry(struct dentry *direntry)
return full_path; return full_path;
} }
struct cifsFileInfo *
cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
struct file *file, struct vfsmount *mnt, unsigned int oflags)
{
int oplock = 0;
struct cifsFileInfo *pCifsFile;
struct cifsInodeInfo *pCifsInode;
struct cifs_sb_info *cifs_sb = CIFS_SB(mnt->mnt_sb);
pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (pCifsFile == NULL)
return pCifsFile;
if (oplockEnabled)
oplock = REQ_OPLOCK;
pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid;
pCifsFile->pInode = igrab(newinode);
pCifsFile->mnt = mnt;
pCifsFile->pfile = file;
pCifsFile->invalidHandle = false;
pCifsFile->closePend = false;
mutex_init(&pCifsFile->fh_mutex);
mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist);
atomic_set(&pCifsFile->count, 1);
INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
write_lock(&GlobalSMBSeslock);
list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
pCifsInode = CIFS_I(newinode);
if (pCifsInode) {
/* if readable file instance put first in list*/
if (oflags & FMODE_READ)
list_add(&pCifsFile->flist, &pCifsInode->openFileList);
else
list_add_tail(&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_unlock(&GlobalSMBSeslock);
file->private_data = pCifsFile;
return pCifsFile;
}
int cifs_posix_open(char *full_path, struct inode **pinode,
struct super_block *sb, int mode, int oflags,
__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;
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;
/* be safe and imply O_SYNC for O_DSYNC */
if (oflags & O_DSYNC)
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;
mode &= ~current_umask();
rc = CIFSPOSIXCreate(xid, cifs_sb->tcon, posix_flags, mode,
pnetfid, presp_data, poplock, 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 */
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;
}
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)
...@@ -291,6 +162,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -291,6 +162,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
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;
struct tcon_link *tlink;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
char *full_path = NULL; char *full_path = NULL;
FILE_ALL_INFO *buf = NULL; FILE_ALL_INFO *buf = NULL;
...@@ -300,21 +172,26 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -300,21 +172,26 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
tcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
full_path = build_path_from_dentry(direntry); FreeXid(xid);
if (full_path == NULL) { return PTR_ERR(tlink);
rc = -ENOMEM;
goto cifs_create_out;
} }
tcon = tlink_tcon(tlink);
if (oplockEnabled) if (oplockEnabled)
oplock = REQ_OPLOCK; oplock = REQ_OPLOCK;
if (nd && (nd->flags & LOOKUP_OPEN)) if (nd && (nd->flags & LOOKUP_OPEN))
oflags = nd->intent.open.flags; oflags = nd->intent.open.file->f_flags;
else else
oflags = FMODE_READ | SMB_O_CREAT; oflags = O_RDONLY | O_CREAT;
full_path = build_path_from_dentry(direntry);
if (full_path == NULL) {
rc = -ENOMEM;
goto cifs_create_out;
}
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
...@@ -344,9 +221,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -344,9 +221,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
/* if the file is going to stay open, then we /* if the file is going to stay open, then we
need to set the desired access properly */ need to set the desired access properly */
desiredAccess = 0; desiredAccess = 0;
if (oflags & FMODE_READ) if (OPEN_FMODE(oflags) & FMODE_READ)
desiredAccess |= GENERIC_READ; /* is this too little? */ desiredAccess |= GENERIC_READ; /* is this too little? */
if (oflags & FMODE_WRITE) if (OPEN_FMODE(oflags) & FMODE_WRITE)
desiredAccess |= GENERIC_WRITE; desiredAccess |= GENERIC_WRITE;
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
...@@ -375,7 +252,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -375,7 +252,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
if (!tcon->unix_ext && (mode & S_IWUGO) == 0) if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
create_options |= CREATE_OPTION_READONLY; create_options |= CREATE_OPTION_READONLY;
if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBOpen(xid, tcon, full_path, disposition, rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
desiredAccess, create_options, desiredAccess, create_options,
&fileHandle, &oplock, buf, cifs_sb->local_nls, &fileHandle, &oplock, buf, cifs_sb->local_nls,
...@@ -467,8 +344,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -467,8 +344,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
goto cifs_create_out; goto cifs_create_out;
} }
pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp, pfile_info = cifs_new_fileinfo(fileHandle, filp, tlink, oplock);
nd->path.mnt, oflags);
if (pfile_info == NULL) { if (pfile_info == NULL) {
fput(filp); fput(filp);
CIFSSMBClose(xid, tcon, fileHandle); CIFSSMBClose(xid, tcon, fileHandle);
...@@ -481,6 +357,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode, ...@@ -481,6 +357,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
cifs_create_out: cifs_create_out:
kfree(buf); kfree(buf);
kfree(full_path); kfree(full_path);
cifs_put_tlink(tlink);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
...@@ -491,6 +368,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, ...@@ -491,6 +368,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
int rc = -EPERM; int rc = -EPERM;
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
char *full_path = NULL; char *full_path = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
...@@ -503,10 +381,14 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, ...@@ -503,10 +381,14 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
if (!old_valid_dev(device_number)) if (!old_valid_dev(device_number))
return -EINVAL; return -EINVAL;
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
...@@ -606,6 +488,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode, ...@@ -606,6 +488,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
kfree(full_path); kfree(full_path);
kfree(buf); kfree(buf);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -619,6 +502,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -619,6 +502,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
__u16 fileHandle = 0; __u16 fileHandle = 0;
bool posix_open = false; bool posix_open = false;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct cifsFileInfo *cfile; struct cifsFileInfo *cfile;
struct inode *newInode = NULL; struct inode *newInode = NULL;
...@@ -633,7 +517,12 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -633,7 +517,12 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
/* check whether path exists */ /* check whether path exists */
cifs_sb = CIFS_SB(parent_dir_inode->i_sb); cifs_sb = CIFS_SB(parent_dir_inode->i_sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
FreeXid(xid);
return (struct dentry *)tlink;
}
pTcon = tlink_tcon(tlink);
/* /*
* Don't allow the separator character in a path component. * Don't allow the separator character in a path component.
...@@ -644,8 +533,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -644,8 +533,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
for (i = 0; i < direntry->d_name.len; i++) for (i = 0; i < direntry->d_name.len; i++)
if (direntry->d_name.name[i] == '\\') { if (direntry->d_name.name[i] == '\\') {
cFYI(1, "Invalid file name"); cFYI(1, "Invalid file name");
FreeXid(xid); rc = -EINVAL;
return ERR_PTR(-EINVAL); goto lookup_out;
} }
} }
...@@ -655,7 +544,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -655,7 +544,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
*/ */
if (nd && (nd->flags & LOOKUP_EXCL)) { if (nd && (nd->flags & LOOKUP_EXCL)) {
d_instantiate(direntry, NULL); d_instantiate(direntry, NULL);
return NULL; rc = 0;
goto lookup_out;
} }
/* can not grab the rename sem here since it would /* can not grab the rename sem here since it would
...@@ -663,8 +553,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -663,8 +553,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
in which we already have the sb rename sem */ in which we already have the sb rename sem */
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
FreeXid(xid); rc = -ENOMEM;
return ERR_PTR(-ENOMEM); goto lookup_out;
} }
if (direntry->d_inode != NULL) { if (direntry->d_inode != NULL) {
...@@ -687,11 +577,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -687,11 +577,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
if (pTcon->unix_ext) { if (pTcon->unix_ext) {
if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) && if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
(nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open && (nd->flags & LOOKUP_OPEN) && !pTcon->broken_posix_open &&
(nd->intent.open.flags & O_CREAT)) { (nd->intent.open.file->f_flags & O_CREAT)) {
rc = cifs_posix_open(full_path, &newInode, rc = cifs_posix_open(full_path, &newInode,
parent_dir_inode->i_sb, parent_dir_inode->i_sb,
nd->intent.open.create_mode, nd->intent.open.create_mode,
nd->intent.open.flags, &oplock, nd->intent.open.file->f_flags, &oplock,
&fileHandle, xid); &fileHandle, xid);
/* /*
* The check below works around a bug in POSIX * The check below works around a bug in POSIX
...@@ -727,9 +617,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -727,9 +617,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
goto lookup_out; goto lookup_out;
} }
cfile = cifs_new_fileinfo(newInode, fileHandle, filp, cfile = cifs_new_fileinfo(fileHandle, filp, tlink,
nd->path.mnt, oplock);
nd->intent.open.flags);
if (cfile == NULL) { if (cfile == NULL) {
fput(filp); fput(filp);
CIFSSMBClose(xid, pTcon, fileHandle); CIFSSMBClose(xid, pTcon, fileHandle);
...@@ -759,6 +648,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -759,6 +648,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
lookup_out: lookup_out:
kfree(full_path); kfree(full_path);
cifs_put_tlink(tlink);
FreeXid(xid); FreeXid(xid);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
......
...@@ -60,34 +60,32 @@ static inline int cifs_convert_flags(unsigned int flags) ...@@ -60,34 +60,32 @@ static inline int cifs_convert_flags(unsigned int flags)
FILE_READ_DATA); 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) if ((flags & O_ACCMODE) == O_RDONLY)
posix_flags = FMODE_READ; posix_flags = SMB_O_RDONLY;
else if ((flags & O_ACCMODE) == O_WRONLY) else if ((flags & O_ACCMODE) == O_WRONLY)
posix_flags = FMODE_WRITE; posix_flags = SMB_O_WRONLY;
else if ((flags & O_ACCMODE) == O_RDWR) { else if ((flags & O_ACCMODE) == O_RDWR)
/* GENERIC_ALL is too much permission to request posix_flags = SMB_O_RDWR;
can cause unnecessary access denied on create */
/* return GENERIC_ALL; */ if (flags & O_CREAT)
posix_flags = FMODE_READ | FMODE_WRITE; posix_flags |= SMB_O_CREAT;
} if (flags & O_EXCL)
/* can not map O_CREAT or O_EXCL or O_TRUNC flags when posix_flags |= SMB_O_EXCL;
reopening a file. They had their effect on the original open */ if (flags & O_TRUNC)
if (flags & O_APPEND) posix_flags |= SMB_O_TRUNC;
posix_flags |= (fmode_t)O_APPEND; /* be safe and imply O_SYNC for O_DSYNC */
if (flags & O_DSYNC) if (flags & O_DSYNC)
posix_flags |= (fmode_t)O_DSYNC; posix_flags |= SMB_O_SYNC;
if (flags & __O_SYNC)
posix_flags |= (fmode_t)__O_SYNC;
if (flags & O_DIRECTORY) if (flags & O_DIRECTORY)
posix_flags |= (fmode_t)O_DIRECTORY; posix_flags |= SMB_O_DIRECTORY;
if (flags & O_NOFOLLOW) if (flags & O_NOFOLLOW)
posix_flags |= (fmode_t)O_NOFOLLOW; posix_flags |= SMB_O_NOFOLLOW;
if (flags & O_DIRECT) if (flags & O_DIRECT)
posix_flags |= (fmode_t)O_DIRECT; posix_flags |= SMB_O_DIRECT;
return posix_flags; return posix_flags;
} }
...@@ -106,66 +104,8 @@ static inline int cifs_get_disposition(unsigned int flags) ...@@ -106,66 +104,8 @@ static inline int cifs_get_disposition(unsigned int flags)
return FILE_OPEN; return FILE_OPEN;
} }
/* all arguments to this function must be checked for validity in caller */
static inline int
cifs_posix_open_inode_helper(struct inode *inode, struct file *file,
struct cifsInodeInfo *pCifsInode, __u32 oplock,
u16 netfid)
{
write_lock(&GlobalSMBSeslock);
pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
if (pCifsInode == NULL) {
write_unlock(&GlobalSMBSeslock);
return -EINVAL;
}
if (pCifsInode->clientCanCacheRead) {
/* we have the inode open somewhere else
no need to discard cache data */
goto psx_client_can_cache;
}
/* BB FIXME need to fix this check to move it earlier into posix_open
BB fIX following section BB FIXME */
/* if not oplocked, invalidate inode pages if mtime or file
size changed */
/* temp = cifs_NTtimeToUnix(le64_to_cpu(buf->LastWriteTime));
if (timespec_equal(&file->f_path.dentry->d_inode->i_mtime, &temp) &&
(file->f_path.dentry->d_inode->i_size ==
(loff_t)le64_to_cpu(buf->EndOfFile))) {
cFYI(1, "inode unchanged on server");
} else {
if (file->f_path.dentry->d_inode->i_mapping) {
rc = filemap_write_and_wait(file->f_path.dentry->d_inode->i_mapping);
if (rc != 0)
CIFS_I(file->f_path.dentry->d_inode)->write_behind_rc = rc;
}
cFYI(1, "invalidating remote inode since open detected it "
"changed");
invalidate_remote_inode(file->f_path.dentry->d_inode);
} */
psx_client_can_cache:
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, "Exclusive Oplock granted on inode %p",
file->f_path.dentry->d_inode);
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
/* will have to change the unlock if we reenable the
filemap_fdatawrite (which does not seem necessary */
write_unlock(&GlobalSMBSeslock);
return 0;
}
/* all arguments to this function must be checked for validity in caller */
static inline int cifs_open_inode_helper(struct inode *inode, static inline int cifs_open_inode_helper(struct inode *inode,
struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf, struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf,
char *full_path, int xid) char *full_path, int xid)
{ {
struct cifsInodeInfo *pCifsInode = CIFS_I(inode); struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
...@@ -207,16 +147,175 @@ static inline int cifs_open_inode_helper(struct inode *inode, ...@@ -207,16 +147,175 @@ static inline int cifs_open_inode_helper(struct inode *inode,
rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
xid, NULL); xid, NULL);
if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) { if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheRead = true;
cFYI(1, "Exclusive Oplock granted on inode %p", inode); cFYI(1, "Exclusive Oplock granted on inode %p", inode);
} else if ((*oplock & 0xF) == OPLOCK_READ) } else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheRead = true;
return rc; 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;
}
struct cifsFileInfo *
cifs_new_fileinfo(__u16 fileHandle, struct file *file,
struct tcon_link *tlink, __u32 oplock)
{
struct dentry *dentry = file->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
struct cifsFileInfo *pCifsFile;
pCifsFile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (pCifsFile == NULL)
return pCifsFile;
pCifsFile->netfid = fileHandle;
pCifsFile->pid = current->tgid;
pCifsFile->uid = current_fsuid();
pCifsFile->dentry = dget(dentry);
pCifsFile->f_flags = file->f_flags;
pCifsFile->invalidHandle = false;
pCifsFile->tlink = cifs_get_tlink(tlink);
mutex_init(&pCifsFile->fh_mutex);
mutex_init(&pCifsFile->lock_mutex);
INIT_LIST_HEAD(&pCifsFile->llist);
atomic_set(&pCifsFile->count, 1);
INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
spin_lock(&cifs_file_list_lock);
list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
/* if readable file instance put first in list*/
if (file->f_mode & FMODE_READ)
list_add(&pCifsFile->flist, &pCifsInode->openFileList);
else
list_add_tail(&pCifsFile->flist, &pCifsInode->openFileList);
spin_unlock(&cifs_file_list_lock);
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true;
cFYI(1, "Exclusive Oplock inode %p", inode);
} else if ((oplock & 0xF) == OPLOCK_READ)
pCifsInode->clientCanCacheRead = true;
file->private_data = pCifsFile;
return pCifsFile;
}
/*
* Release a reference on the file private data. This may involve closing
* the filehandle out on the server.
*/
void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
{
struct cifsTconInfo *tcon = tlink_tcon(cifs_file->tlink);
struct cifsInodeInfo *cifsi = CIFS_I(cifs_file->dentry->d_inode);
struct cifsLockInfo *li, *tmp;
spin_lock(&cifs_file_list_lock);
if (!atomic_dec_and_test(&cifs_file->count)) {
spin_unlock(&cifs_file_list_lock);
return;
}
/* remove it from the lists */
list_del(&cifs_file->flist);
list_del(&cifs_file->tlist);
if (list_empty(&cifsi->openFileList)) {
cFYI(1, "closing last open instance for inode %p",
cifs_file->dentry->d_inode);
cifsi->clientCanCacheRead = false;
cifsi->clientCanCacheAll = false;
}
spin_unlock(&cifs_file_list_lock);
if (!tcon->need_reconnect && !cifs_file->invalidHandle) {
int xid, rc;
xid = GetXid();
rc = CIFSSMBClose(xid, tcon, cifs_file->netfid);
FreeXid(xid);
}
/* Delete any outstanding lock records. We'll lose them when the file
* is closed anyway.
*/
mutex_lock(&cifs_file->lock_mutex);
list_for_each_entry_safe(li, tmp, &cifs_file->llist, llist) {
list_del(&li->llist);
kfree(li);
}
mutex_unlock(&cifs_file->lock_mutex);
cifs_put_tlink(cifs_file->tlink);
dput(cifs_file->dentry);
kfree(cifs_file);
}
int cifs_open(struct inode *inode, struct file *file) int cifs_open(struct inode *inode, struct file *file)
{ {
int rc = -EACCES; int rc = -EACCES;
...@@ -224,6 +323,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -224,6 +323,7 @@ int cifs_open(struct inode *inode, struct file *file)
__u32 oplock; __u32 oplock;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct tcon_link *tlink;
struct cifsFileInfo *pCifsFile = NULL; struct cifsFileInfo *pCifsFile = NULL;
struct cifsInodeInfo *pCifsInode; struct cifsInodeInfo *pCifsInode;
char *full_path = NULL; char *full_path = NULL;
...@@ -235,7 +335,12 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -235,7 +335,12 @@ int cifs_open(struct inode *inode, struct file *file)
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
tcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
FreeXid(xid);
return PTR_ERR(tlink);
}
tcon = tlink_tcon(tlink);
pCifsInode = CIFS_I(file->f_path.dentry->d_inode); pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
...@@ -257,27 +362,15 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -257,27 +362,15 @@ int cifs_open(struct inode *inode, struct file *file)
(tcon->ses->capabilities & CAP_UNIX) && (tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { 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 */ /* can not refresh inode info since size could be stale */
rc = cifs_posix_open(full_path, &inode, inode->i_sb, rc = cifs_posix_open(full_path, &inode, inode->i_sb,
cifs_sb->mnt_file_mode /* ignored */, cifs_sb->mnt_file_mode /* ignored */,
oflags, &oplock, &netfid, xid); file->f_flags, &oplock, &netfid, xid);
if (rc == 0) { if (rc == 0) {
cFYI(1, "posix open succeeded"); cFYI(1, "posix open succeeded");
/* no need for special case handling of setting mode
on read only files needed here */
rc = cifs_posix_open_inode_helper(inode, file,
pCifsInode, oplock, netfid);
if (rc != 0) {
CIFSSMBClose(xid, tcon, netfid);
goto out;
}
pCifsFile = cifs_new_fileinfo(inode, netfid, file, pCifsFile = cifs_new_fileinfo(netfid, file, tlink,
file->f_path.mnt, oplock);
oflags);
if (pCifsFile == NULL) { if (pCifsFile == NULL) {
CIFSSMBClose(xid, tcon, netfid); CIFSSMBClose(xid, tcon, netfid);
rc = -ENOMEM; rc = -ENOMEM;
...@@ -345,7 +438,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -345,7 +438,7 @@ int cifs_open(struct inode *inode, struct file *file)
goto out; goto out;
} }
if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS) if (tcon->ses->capabilities & CAP_NT_SMBS)
rc = CIFSSMBOpen(xid, tcon, full_path, disposition, rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf, desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
...@@ -365,12 +458,11 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -365,12 +458,11 @@ int cifs_open(struct inode *inode, struct file *file)
goto out; goto out;
} }
rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid); rc = cifs_open_inode_helper(inode, tcon, oplock, buf, full_path, xid);
if (rc != 0) if (rc != 0)
goto out; goto out;
pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt, pCifsFile = cifs_new_fileinfo(netfid, file, tlink, oplock);
file->f_flags);
if (pCifsFile == NULL) { if (pCifsFile == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
...@@ -402,6 +494,7 @@ int cifs_open(struct inode *inode, struct file *file) ...@@ -402,6 +494,7 @@ int cifs_open(struct inode *inode, struct file *file)
kfree(buf); kfree(buf);
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -416,14 +509,13 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile) ...@@ -416,14 +509,13 @@ static int cifs_relock_file(struct cifsFileInfo *cifsFile)
return rc; return rc;
} }
static int cifs_reopen_file(struct file *file, bool can_flush) static int cifs_reopen_file(struct cifsFileInfo *pCifsFile, bool can_flush)
{ {
int rc = -EACCES; int rc = -EACCES;
int xid; int xid;
__u32 oplock; __u32 oplock;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct cifsFileInfo *pCifsFile;
struct cifsInodeInfo *pCifsInode; struct cifsInodeInfo *pCifsInode;
struct inode *inode; struct inode *inode;
char *full_path = NULL; char *full_path = NULL;
...@@ -431,11 +523,6 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -431,11 +523,6 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
int disposition = FILE_OPEN; int disposition = FILE_OPEN;
__u16 netfid; __u16 netfid;
if (file->private_data)
pCifsFile = file->private_data;
else
return -EBADF;
xid = GetXid(); xid = GetXid();
mutex_lock(&pCifsFile->fh_mutex); mutex_lock(&pCifsFile->fh_mutex);
if (!pCifsFile->invalidHandle) { if (!pCifsFile->invalidHandle) {
...@@ -445,39 +532,24 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -445,39 +532,24 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
return rc; return rc;
} }
if (file->f_path.dentry == NULL) { inode = pCifsFile->dentry->d_inode;
cERROR(1, "no valid name if dentry freed");
dump_stack();
rc = -EBADF;
goto reopen_error_exit;
}
inode = file->f_path.dentry->d_inode;
if (inode == NULL) {
cERROR(1, "inode not valid");
dump_stack();
rc = -EBADF;
goto reopen_error_exit;
}
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
tcon = cifs_sb->tcon; tcon = tlink_tcon(pCifsFile->tlink);
/* can not grab rename sem here because various ops, including /* can not grab rename sem here because various ops, including
those that already have the rename sem can end up causing writepage those that already have the rename sem can end up causing writepage
to get called and if the server was down that means we end up here, to get called and if the server was down that means we end up here,
and we can never tell if the caller already has the rename_sem */ and we can never tell if the caller already has the rename_sem */
full_path = build_path_from_dentry(file->f_path.dentry); full_path = build_path_from_dentry(pCifsFile->dentry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
reopen_error_exit:
mutex_unlock(&pCifsFile->fh_mutex); mutex_unlock(&pCifsFile->fh_mutex);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
cFYI(1, "inode = 0x%p file flags 0x%x for %s", cFYI(1, "inode = 0x%p file flags 0x%x for %s",
inode, file->f_flags, full_path); inode, pCifsFile->f_flags, full_path);
if (oplockEnabled) if (oplockEnabled)
oplock = REQ_OPLOCK; oplock = REQ_OPLOCK;
...@@ -487,8 +559,14 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -487,8 +559,14 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) && if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP & (CIFS_UNIX_POSIX_PATH_OPS_CAP &
le64_to_cpu(tcon->fsUnixInfo.Capability))) { 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 = pCifsFile->f_flags &
~(O_CREAT | O_EXCL | O_TRUNC);
rc = cifs_posix_open(full_path, NULL, inode->i_sb, rc = cifs_posix_open(full_path, NULL, inode->i_sb,
cifs_sb->mnt_file_mode /* ignored */, cifs_sb->mnt_file_mode /* ignored */,
oflags, &oplock, &netfid, xid); oflags, &oplock, &netfid, xid);
...@@ -500,7 +578,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -500,7 +578,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
in the reconnect path it is important to retry hard */ in the reconnect path it is important to retry hard */
} }
desiredAccess = cifs_convert_flags(file->f_flags); desiredAccess = cifs_convert_flags(pCifsFile->f_flags);
/* Can not refresh inode by passing in file_info buf to be returned /* Can not refresh inode by passing in file_info buf to be returned
by SMBOpen and then calling get_inode_info with returned buf by SMBOpen and then calling get_inode_info with returned buf
...@@ -516,19 +594,20 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -516,19 +594,20 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
mutex_unlock(&pCifsFile->fh_mutex); mutex_unlock(&pCifsFile->fh_mutex);
cFYI(1, "cifs_open returned 0x%x", rc); cFYI(1, "cifs_open returned 0x%x", rc);
cFYI(1, "oplock: %d", oplock); cFYI(1, "oplock: %d", oplock);
} else { goto reopen_error_exit;
}
reopen_success: reopen_success:
pCifsFile->netfid = netfid; pCifsFile->netfid = netfid;
pCifsFile->invalidHandle = false; pCifsFile->invalidHandle = false;
mutex_unlock(&pCifsFile->fh_mutex); mutex_unlock(&pCifsFile->fh_mutex);
pCifsInode = CIFS_I(inode); pCifsInode = CIFS_I(inode);
if (pCifsInode) {
if (can_flush) { if (can_flush) {
rc = filemap_write_and_wait(inode->i_mapping); rc = filemap_write_and_wait(inode->i_mapping);
if (rc != 0) if (rc != 0)
CIFS_I(inode)->write_behind_rc = rc; CIFS_I(inode)->write_behind_rc = rc;
/* temporarily disable caching while we
go to server to get inode info */
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
pCifsInode->clientCanCacheRead = false; pCifsInode->clientCanCacheRead = false;
if (tcon->unix_ext) if (tcon->unix_ext)
...@@ -548,7 +627,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -548,7 +627,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
pCifsInode->clientCanCacheAll = true; pCifsInode->clientCanCacheAll = true;
pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheRead = true;
cFYI(1, "Exclusive Oplock granted on inode %p", cFYI(1, "Exclusive Oplock granted on inode %p",
file->f_path.dentry->d_inode); pCifsFile->dentry->d_inode);
} else if ((oplock & 0xF) == OPLOCK_READ) { } else if ((oplock & 0xF) == OPLOCK_READ) {
pCifsInode->clientCanCacheRead = true; pCifsInode->clientCanCacheRead = true;
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
...@@ -557,8 +636,8 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -557,8 +636,8 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
} }
cifs_relock_file(pCifsFile); cifs_relock_file(pCifsFile);
}
} reopen_error_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
...@@ -566,79 +645,11 @@ static int cifs_reopen_file(struct file *file, bool can_flush) ...@@ -566,79 +645,11 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
int cifs_close(struct inode *inode, struct file *file) int cifs_close(struct inode *inode, struct file *file)
{ {
int rc = 0;
int xid, timeout;
struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
struct cifsFileInfo *pSMBFile = file->private_data;
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
if (pSMBFile) {
struct cifsLockInfo *li, *tmp;
write_lock(&GlobalSMBSeslock);
pSMBFile->closePend = true;
if (pTcon) {
/* no sense reconnecting to close a file that is
already closed */
if (!pTcon->need_reconnect) {
write_unlock(&GlobalSMBSeslock);
timeout = 2;
while ((atomic_read(&pSMBFile->count) != 1)
&& (timeout <= 2048)) {
/* Give write a better chance to get to
server ahead of the close. We do not
want to add a wait_q here as it would
increase the memory utilization as
the struct would be in each open file,
but this should give enough time to
clear the socket */
cFYI(DBG2, "close delay, write pending");
msleep(timeout);
timeout *= 4;
}
if (!pTcon->need_reconnect &&
!pSMBFile->invalidHandle)
rc = CIFSSMBClose(xid, pTcon,
pSMBFile->netfid);
} else
write_unlock(&GlobalSMBSeslock);
} else
write_unlock(&GlobalSMBSeslock);
/* Delete any outstanding lock records.
We'll lose them when the file is closed anyway. */
mutex_lock(&pSMBFile->lock_mutex);
list_for_each_entry_safe(li, tmp, &pSMBFile->llist, llist) {
list_del(&li->llist);
kfree(li);
}
mutex_unlock(&pSMBFile->lock_mutex);
write_lock(&GlobalSMBSeslock);
list_del(&pSMBFile->flist);
list_del(&pSMBFile->tlist);
write_unlock(&GlobalSMBSeslock);
cifsFileInfo_put(file->private_data); cifsFileInfo_put(file->private_data);
file->private_data = NULL; file->private_data = NULL;
} else
rc = -EBADF;
read_lock(&GlobalSMBSeslock); /* return code from the ->release op is always ignored */
if (list_empty(&(CIFS_I(inode)->openFileList))) { return 0;
cFYI(1, "closing last open instance for inode %p", inode);
/* if the file is not open we do not know if we can cache info
on this inode, much less write behind and read ahead */
CIFS_I(inode)->clientCanCacheRead = false;
CIFS_I(inode)->clientCanCacheAll = false;
}
read_unlock(&GlobalSMBSeslock);
if ((rc == 0) && CIFS_I(inode)->write_behind_rc)
rc = CIFS_I(inode)->write_behind_rc;
FreeXid(xid);
return rc;
} }
int cifs_closedir(struct inode *inode, struct file *file) int cifs_closedir(struct inode *inode, struct file *file)
...@@ -653,25 +664,21 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -653,25 +664,21 @@ int cifs_closedir(struct inode *inode, struct file *file)
xid = GetXid(); xid = GetXid();
if (pCFileStruct) { if (pCFileStruct) {
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink);
struct cifs_sb_info *cifs_sb =
CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon;
cFYI(1, "Freeing private data in close dir"); cFYI(1, "Freeing private data in close dir");
write_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
if (!pCFileStruct->srch_inf.endOfSearch && if (!pCFileStruct->srch_inf.endOfSearch &&
!pCFileStruct->invalidHandle) { !pCFileStruct->invalidHandle) {
pCFileStruct->invalidHandle = true; pCFileStruct->invalidHandle = true;
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid); rc = CIFSFindClose(xid, pTcon, pCFileStruct->netfid);
cFYI(1, "Closing uncompleted readdir with rc %d", cFYI(1, "Closing uncompleted readdir with rc %d",
rc); rc);
/* not much we can do if it fails anyway, ignore rc */ /* not much we can do if it fails anyway, ignore rc */
rc = 0; rc = 0;
} else } else
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
ptmp = pCFileStruct->srch_inf.ntwrk_buf_start; ptmp = pCFileStruct->srch_inf.ntwrk_buf_start;
if (ptmp) { if (ptmp) {
cFYI(1, "closedir free smb buf in srch struct"); cFYI(1, "closedir free smb buf in srch struct");
...@@ -681,6 +688,7 @@ int cifs_closedir(struct inode *inode, struct file *file) ...@@ -681,6 +688,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
else else
cifs_buf_release(ptmp); cifs_buf_release(ptmp);
} }
cifs_put_tlink(pCFileStruct->tlink);
kfree(file->private_data); kfree(file->private_data);
file->private_data = NULL; file->private_data = NULL;
} }
...@@ -767,7 +775,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock) ...@@ -767,7 +775,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
cFYI(1, "Unknown type of lock"); cFYI(1, "Unknown type of lock");
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
tcon = cifs_sb->tcon; tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink);
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -EBADF; rc = -EBADF;
...@@ -960,14 +968,14 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -960,14 +968,14 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon;
/* cFYI(1, " write %d bytes to offset %lld of %s", write_size, /* cFYI(1, " write %d bytes to offset %lld of %s", write_size,
*poffset, file->f_path.dentry->d_name.name); */ *poffset, file->f_path.dentry->d_name.name); */
if (file->private_data == NULL) if (file->private_data == NULL)
return -EBADF; return -EBADF;
open_file = file->private_data; open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
rc = generic_write_checks(file, poffset, &write_size, 0); rc = generic_write_checks(file, poffset, &write_size, 0);
if (rc) if (rc)
...@@ -988,19 +996,12 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -988,19 +996,12 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
we blocked so return what we managed to write */ we blocked so return what we managed to write */
return total_written; return total_written;
} }
if (open_file->closePend) {
FreeXid(xid);
if (total_written)
return total_written;
else
return -EBADF;
}
if (open_file->invalidHandle) { if (open_file->invalidHandle) {
/* we could deadlock if we called /* we could deadlock if we called
filemap_fdatawait from here so tell filemap_fdatawait from here so tell
reopen_file not to flush data to server reopen_file not to flush data to server
now */ now */
rc = cifs_reopen_file(file, false); rc = cifs_reopen_file(open_file, false);
if (rc != 0) if (rc != 0)
break; break;
} }
...@@ -1048,8 +1049,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data, ...@@ -1048,8 +1049,9 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
return total_written; return total_written;
} }
static ssize_t cifs_write(struct file *file, const char *write_data, static ssize_t cifs_write(struct cifsFileInfo *open_file,
size_t write_size, loff_t *poffset) const char *write_data, size_t write_size,
loff_t *poffset)
{ {
int rc = 0; int rc = 0;
unsigned int bytes_written = 0; unsigned int bytes_written = 0;
...@@ -1057,19 +1059,15 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1057,19 +1059,15 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
int xid, long_op; int xid, long_op;
struct cifsFileInfo *open_file; struct dentry *dentry = open_file->dentry;
struct cifsInodeInfo *cifsi = CIFS_I(file->f_path.dentry->d_inode); struct cifsInodeInfo *cifsi = CIFS_I(dentry->d_inode);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon; cifs_sb = CIFS_SB(dentry->d_sb);
cFYI(1, "write %zd bytes to offset %lld of %s", write_size, cFYI(1, "write %zd bytes to offset %lld of %s", write_size,
*poffset, file->f_path.dentry->d_name.name); *poffset, dentry->d_name.name);
if (file->private_data == NULL) pTcon = tlink_tcon(open_file->tlink);
return -EBADF;
open_file = file->private_data;
xid = GetXid(); xid = GetXid();
...@@ -1078,28 +1076,12 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1078,28 +1076,12 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
total_written += bytes_written) { total_written += bytes_written) {
rc = -EAGAIN; rc = -EAGAIN;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
if (file->private_data == NULL) {
/* file has been closed on us */
FreeXid(xid);
/* if we have gotten here we have written some data
and blocked, and the file has been freed on us
while we blocked so return what we managed to
write */
return total_written;
}
if (open_file->closePend) {
FreeXid(xid);
if (total_written)
return total_written;
else
return -EBADF;
}
if (open_file->invalidHandle) { if (open_file->invalidHandle) {
/* we could deadlock if we called /* we could deadlock if we called
filemap_fdatawait from here so tell filemap_fdatawait from here so tell
reopen_file not to flush data to reopen_file not to flush data to
server now */ server now */
rc = cifs_reopen_file(file, false); rc = cifs_reopen_file(open_file, false);
if (rc != 0) if (rc != 0)
break; break;
} }
...@@ -1146,43 +1128,41 @@ static ssize_t cifs_write(struct file *file, const char *write_data, ...@@ -1146,43 +1128,41 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
cifs_stats_bytes_written(pTcon, total_written); cifs_stats_bytes_written(pTcon, total_written);
/* since the write may have blocked check these pointers again */
if ((file->f_path.dentry) && (file->f_path.dentry->d_inode)) {
/*BB We could make this contingent on superblock ATIME flag too */
/* file->f_path.dentry->d_inode->i_ctime =
file->f_path.dentry->d_inode->i_mtime = CURRENT_TIME;*/
if (total_written > 0) { if (total_written > 0) {
spin_lock(&file->f_path.dentry->d_inode->i_lock); spin_lock(&dentry->d_inode->i_lock);
if (*poffset > file->f_path.dentry->d_inode->i_size) if (*poffset > dentry->d_inode->i_size)
i_size_write(file->f_path.dentry->d_inode, i_size_write(dentry->d_inode, *poffset);
*poffset); spin_unlock(&dentry->d_inode->i_lock);
spin_unlock(&file->f_path.dentry->d_inode->i_lock);
}
mark_inode_dirty_sync(file->f_path.dentry->d_inode);
} }
mark_inode_dirty_sync(dentry->d_inode);
FreeXid(xid); FreeXid(xid);
return total_written; return total_written;
} }
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only)
{ {
struct cifsFileInfo *open_file = NULL; struct cifsFileInfo *open_file = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
read_lock(&GlobalSMBSeslock); /* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
spin_lock(&cifs_file_list_lock);
/* we could simply get the first_list_entry since write-only entries /* we could simply get the first_list_entry since write-only entries
are always at the end of the list but since the first entry might are always at the end of the list but since the first entry might
have a close pending, we go through the whole list */ have a close pending, we go through the whole list */
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (open_file->closePend) if (fsuid_only && open_file->uid != current_fsuid())
continue; continue;
if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) || if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
(open_file->pfile->f_flags & O_RDONLY))) {
if (!open_file->invalidHandle) { if (!open_file->invalidHandle) {
/* found a good file */ /* found a good file */
/* lock it so it will not be closed on us */ /* lock it so it will not be closed on us */
cifsFileInfo_get(open_file); cifsFileInfo_get(open_file);
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
return open_file; return open_file;
} /* else might as well continue, and look for } /* else might as well continue, and look for
another, or simply have the caller reopen it another, or simply have the caller reopen it
...@@ -1190,14 +1170,16 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1190,14 +1170,16 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
} else /* write only file */ } else /* write only file */
break; /* write only files are last so must be done */ break; /* write only files are last so must be done */
} }
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
return NULL; return NULL;
} }
#endif #endif
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
bool fsuid_only)
{ {
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
bool any_available = false; bool any_available = false;
int rc; int rc;
...@@ -1211,53 +1193,39 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1211,53 +1193,39 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
return NULL; return NULL;
} }
read_lock(&GlobalSMBSeslock); /* only filter by fsuid on multiuser mounts */
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
fsuid_only = false;
spin_lock(&cifs_file_list_lock);
refind_writable: refind_writable:
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (open_file->closePend || if (!any_available && open_file->pid != current->tgid)
(!any_available && open_file->pid != current->tgid))
continue; continue;
if (fsuid_only && open_file->uid != current_fsuid())
if (open_file->pfile && continue;
((open_file->pfile->f_flags & O_RDWR) || if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
(open_file->pfile->f_flags & O_WRONLY))) {
cifsFileInfo_get(open_file); cifsFileInfo_get(open_file);
if (!open_file->invalidHandle) { if (!open_file->invalidHandle) {
/* found a good writable file */ /* found a good writable file */
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
return open_file; return open_file;
} }
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
/* Had to unlock since following call can block */ /* Had to unlock since following call can block */
rc = cifs_reopen_file(open_file->pfile, false); rc = cifs_reopen_file(open_file, false);
if (!rc) { if (!rc)
if (!open_file->closePend)
return open_file; return open_file;
else { /* start over in case this was deleted */
/* since the list could be modified */
read_lock(&GlobalSMBSeslock);
cifsFileInfo_put(open_file);
goto refind_writable;
}
}
/* if it fails, try another handle if possible - /* if it fails, try another handle if possible */
(we can not do this if closePending since
loop could be modified - in which case we
have to start at the beginning of the list
again. Note that it would be bad
to hold up writepages here (rather than
in caller) with continuous retries */
cFYI(1, "wp failed on reopen file"); cFYI(1, "wp failed on reopen file");
read_lock(&GlobalSMBSeslock);
/* can not use this handle, no write
pending on this one after all */
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
if (open_file->closePend) /* list could have changed */ spin_lock(&cifs_file_list_lock);
goto refind_writable;
/* else we simply continue to the next entry. Thus /* else we simply continue to the next entry. Thus
we do not loop on reopen errors. If we we do not loop on reopen errors. If we
can not reopen the file, for example if we can not reopen the file, for example if we
...@@ -1272,7 +1240,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode) ...@@ -1272,7 +1240,7 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
any_available = true; any_available = true;
goto refind_writable; goto refind_writable;
} }
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
return NULL; return NULL;
} }
...@@ -1284,7 +1252,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) ...@@ -1284,7 +1252,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
int rc = -EFAULT; int rc = -EFAULT;
int bytes_written = 0; int bytes_written = 0;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifsTconInfo *pTcon;
struct inode *inode; struct inode *inode;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
...@@ -1293,7 +1260,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) ...@@ -1293,7 +1260,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
inode = page->mapping->host; inode = page->mapping->host;
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
offset += (loff_t)from; offset += (loff_t)from;
write_data = kmap(page); write_data = kmap(page);
...@@ -1314,10 +1280,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to) ...@@ -1314,10 +1280,10 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
if (mapping->host->i_size - offset < (loff_t)to) if (mapping->host->i_size - offset < (loff_t)to)
to = (unsigned)(mapping->host->i_size - offset); to = (unsigned)(mapping->host->i_size - offset);
open_file = find_writable_file(CIFS_I(mapping->host)); open_file = find_writable_file(CIFS_I(mapping->host), false);
if (open_file) { if (open_file) {
bytes_written = cifs_write(open_file->pfile, write_data, bytes_written = cifs_write(open_file, write_data,
to-from, &offset); to - from, &offset);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
/* Does mm or vfs already set times? */ /* Does mm or vfs already set times? */
inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb); inode->i_atime = inode->i_mtime = current_fs_time(inode->i_sb);
...@@ -1352,6 +1318,7 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1352,6 +1318,7 @@ static int cifs_writepages(struct address_space *mapping,
int nr_pages; int nr_pages;
__u64 offset = 0; __u64 offset = 0;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsTconInfo *tcon;
struct cifsInodeInfo *cifsi = CIFS_I(mapping->host); struct cifsInodeInfo *cifsi = CIFS_I(mapping->host);
struct page *page; struct page *page;
struct pagevec pvec; struct pagevec pvec;
...@@ -1359,6 +1326,15 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1359,6 +1326,15 @@ static int cifs_writepages(struct address_space *mapping,
int scanned = 0; int scanned = 0;
int xid, long_op; int xid, long_op;
/*
* BB: Is this meaningful for a non-block-device file system?
* If it is, we should test it again after we do I/O
*/
if (wbc->nonblocking && bdi_write_congested(bdi)) {
wbc->encountered_congestion = 1;
return 0;
}
cifs_sb = CIFS_SB(mapping->host->i_sb); cifs_sb = CIFS_SB(mapping->host->i_sb);
/* /*
...@@ -1368,26 +1344,28 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1368,26 +1344,28 @@ static int cifs_writepages(struct address_space *mapping,
if (cifs_sb->wsize < PAGE_CACHE_SIZE) if (cifs_sb->wsize < PAGE_CACHE_SIZE)
return generic_writepages(mapping, wbc); return generic_writepages(mapping, wbc);
if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
if (cifs_sb->tcon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
if (!experimEnabled)
return generic_writepages(mapping, wbc);
iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL); iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
if (iov == NULL) if (iov == NULL)
return generic_writepages(mapping, wbc); return generic_writepages(mapping, wbc);
/* /*
* BB: Is this meaningful for a non-block-device file system? * if there's no open file, then this is likely to fail too,
* If it is, we should test it again after we do I/O * but it'll at least handle the return. Maybe it should be
* a BUG() instead?
*/ */
if (wbc->nonblocking && bdi_write_congested(bdi)) { open_file = find_writable_file(CIFS_I(mapping->host), false);
wbc->encountered_congestion = 1; if (!open_file) {
kfree(iov); kfree(iov);
return 0; return generic_writepages(mapping, wbc);
}
tcon = tlink_tcon(open_file->tlink);
if (!experimEnabled && tcon->ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
cifsFileInfo_put(open_file);
return generic_writepages(mapping, wbc);
} }
cifsFileInfo_put(open_file);
xid = GetXid(); xid = GetXid();
...@@ -1492,23 +1470,20 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1492,23 +1470,20 @@ static int cifs_writepages(struct address_space *mapping,
break; break;
} }
if (n_iov) { if (n_iov) {
/* Search for a writable handle every time we call open_file = find_writable_file(CIFS_I(mapping->host),
* CIFSSMBWrite2. We can't rely on the last handle false);
* we used to still be valid
*/
open_file = find_writable_file(CIFS_I(mapping->host));
if (!open_file) { if (!open_file) {
cERROR(1, "No writable handles for inode"); cERROR(1, "No writable handles for inode");
rc = -EBADF; rc = -EBADF;
} else { } else {
long_op = cifs_write_timeout(cifsi, offset); long_op = cifs_write_timeout(cifsi, offset);
rc = CIFSSMBWrite2(xid, cifs_sb->tcon, rc = CIFSSMBWrite2(xid, tcon, open_file->netfid,
open_file->netfid,
bytes_to_write, offset, bytes_to_write, offset,
&bytes_written, iov, n_iov, &bytes_written, iov, n_iov,
long_op); long_op);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
cifs_update_eof(cifsi, offset, bytes_written); cifs_update_eof(cifsi, offset, bytes_written);
}
if (rc || bytes_written < bytes_to_write) { if (rc || bytes_written < bytes_to_write) {
cERROR(1, "Write2 ret %d, wrote %d", cERROR(1, "Write2 ret %d, wrote %d",
...@@ -1520,10 +1495,9 @@ static int cifs_writepages(struct address_space *mapping, ...@@ -1520,10 +1495,9 @@ static int cifs_writepages(struct address_space *mapping,
else else
set_bit(AS_EIO, &mapping->flags); set_bit(AS_EIO, &mapping->flags);
} else { } else {
cifs_stats_bytes_written(cifs_sb->tcon, cifs_stats_bytes_written(tcon, bytes_written);
bytes_written);
}
} }
for (i = 0; i < n_iov; i++) { for (i = 0; i < n_iov; i++) {
page = pvec.pages[first + i]; page = pvec.pages[first + i];
/* Should we also set page error on /* Should we also set page error on
...@@ -1624,7 +1598,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping, ...@@ -1624,7 +1598,8 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
/* BB check if anything else missing out of ppw /* BB check if anything else missing out of ppw
such as updating last write time */ such as updating last write time */
page_data = kmap(page); page_data = kmap(page);
rc = cifs_write(file, page_data + offset, copied, &pos); rc = cifs_write(file->private_data, page_data + offset,
copied, &pos);
/* if (rc < 0) should we set writebehind rc? */ /* if (rc < 0) should we set writebehind rc? */
kunmap(page); kunmap(page);
...@@ -1665,7 +1640,7 @@ int cifs_fsync(struct file *file, int datasync) ...@@ -1665,7 +1640,7 @@ int cifs_fsync(struct file *file, int datasync)
if (rc == 0) { if (rc == 0) {
rc = CIFS_I(inode)->write_behind_rc; rc = CIFS_I(inode)->write_behind_rc;
CIFS_I(inode)->write_behind_rc = 0; CIFS_I(inode)->write_behind_rc = 0;
tcon = CIFS_SB(inode->i_sb)->tcon; tcon = tlink_tcon(smbfile->tlink);
if (!rc && tcon && smbfile && if (!rc && tcon && smbfile &&
!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC)) !(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid); rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
...@@ -1750,7 +1725,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1750,7 +1725,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon;
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -EBADF; rc = -EBADF;
...@@ -1758,6 +1732,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1758,6 +1732,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
return rc; return rc;
} }
open_file = file->private_data; open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
if ((file->f_flags & O_ACCMODE) == O_WRONLY) if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, "attempting read on write only file instance"); cFYI(1, "attempting read on write only file instance");
...@@ -1771,9 +1746,8 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data, ...@@ -1771,9 +1746,8 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
smb_read_data = NULL; smb_read_data = NULL;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
int buf_type = CIFS_NO_BUFFER; int buf_type = CIFS_NO_BUFFER;
if ((open_file->invalidHandle) && if (open_file->invalidHandle) {
(!open_file->closePend)) { rc = cifs_reopen_file(open_file, true);
rc = cifs_reopen_file(file, true);
if (rc != 0) if (rc != 0)
break; break;
} }
...@@ -1831,7 +1805,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1831,7 +1805,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon;
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -EBADF; rc = -EBADF;
...@@ -1839,6 +1812,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1839,6 +1812,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
return rc; return rc;
} }
open_file = file->private_data; open_file = file->private_data;
pTcon = tlink_tcon(open_file->tlink);
if ((file->f_flags & O_ACCMODE) == O_WRONLY) if ((file->f_flags & O_ACCMODE) == O_WRONLY)
cFYI(1, "attempting read on write only file instance"); cFYI(1, "attempting read on write only file instance");
...@@ -1857,9 +1831,8 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size, ...@@ -1857,9 +1831,8 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
} }
rc = -EAGAIN; rc = -EAGAIN;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
if ((open_file->invalidHandle) && if (open_file->invalidHandle) {
(!open_file->closePend)) { rc = cifs_reopen_file(open_file, true);
rc = cifs_reopen_file(file, true);
if (rc != 0) if (rc != 0)
break; break;
} }
...@@ -1974,7 +1947,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -1974,7 +1947,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
} }
open_file = file->private_data; open_file = file->private_data;
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon; pTcon = tlink_tcon(open_file->tlink);
/* /*
* Reads as many pages as possible from fscache. Returns -ENOBUFS * Reads as many pages as possible from fscache. Returns -ENOBUFS
...@@ -2022,9 +1995,8 @@ static int cifs_readpages(struct file *file, struct address_space *mapping, ...@@ -2022,9 +1995,8 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
read_size, contig_pages); read_size, contig_pages);
rc = -EAGAIN; rc = -EAGAIN;
while (rc == -EAGAIN) { while (rc == -EAGAIN) {
if ((open_file->invalidHandle) && if (open_file->invalidHandle) {
(!open_file->closePend)) { rc = cifs_reopen_file(open_file, true);
rc = cifs_reopen_file(file, true);
if (rc != 0) if (rc != 0)
break; break;
} }
...@@ -2173,18 +2145,14 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode) ...@@ -2173,18 +2145,14 @@ static int is_inode_writable(struct cifsInodeInfo *cifs_inode)
{ {
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
read_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) { list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
if (open_file->closePend) if (OPEN_FMODE(open_file->f_flags) & FMODE_WRITE) {
continue; spin_unlock(&cifs_file_list_lock);
if (open_file->pfile &&
((open_file->pfile->f_flags & O_RDWR) ||
(open_file->pfile->f_flags & O_WRONLY))) {
read_unlock(&GlobalSMBSeslock);
return 1; return 1;
} }
} }
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
return 0; return 0;
} }
...@@ -2310,9 +2278,8 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -2310,9 +2278,8 @@ void cifs_oplock_break(struct work_struct *work)
{ {
struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo,
oplock_break); oplock_break);
struct inode *inode = cfile->pInode; struct inode *inode = cfile->dentry->d_inode;
struct cifsInodeInfo *cinode = CIFS_I(inode); struct cifsInodeInfo *cinode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb);
int rc, waitrc = 0; int rc, waitrc = 0;
if (inode && S_ISREG(inode->i_mode)) { if (inode && S_ISREG(inode->i_mode)) {
...@@ -2338,9 +2305,9 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -2338,9 +2305,9 @@ void cifs_oplock_break(struct work_struct *work)
* not bother sending an oplock release if session to server still is * not bother sending an oplock release if session to server still is
* disconnected since oplock already released by the server * disconnected since oplock already released by the server
*/ */
if (!cfile->closePend && !cfile->oplock_break_cancelled) { if (!cfile->oplock_break_cancelled) {
rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0, rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0,
LOCKING_ANDX_OPLOCK_RELEASE, false); 0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false);
cFYI(1, "Oplock release rc = %d", rc); cFYI(1, "Oplock release rc = %d", rc);
} }
...@@ -2349,22 +2316,22 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -2349,22 +2316,22 @@ void cifs_oplock_break(struct work_struct *work)
* finished grabbing reference for us. Make sure it's done by * finished grabbing reference for us. Make sure it's done by
* waiting for GlobalSMSSeslock. * waiting for GlobalSMSSeslock.
*/ */
write_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
cifs_oplock_break_put(cfile); cifs_oplock_break_put(cfile);
} }
void cifs_oplock_break_get(struct cifsFileInfo *cfile) void cifs_oplock_break_get(struct cifsFileInfo *cfile)
{ {
mntget(cfile->mnt); cifs_sb_active(cfile->dentry->d_sb);
cifsFileInfo_get(cfile); cifsFileInfo_get(cfile);
} }
void cifs_oplock_break_put(struct cifsFileInfo *cfile) void cifs_oplock_break_put(struct cifsFileInfo *cfile)
{ {
mntput(cfile->mnt);
cifsFileInfo_put(cfile); cifsFileInfo_put(cfile);
cifs_sb_deactive(cfile->dentry->d_sb);
} }
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
......
...@@ -62,15 +62,15 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode) ...@@ -62,15 +62,15 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode)
{ {
struct cifsInodeInfo *cifsi = CIFS_I(inode); struct cifsInodeInfo *cifsi = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
if (cifsi->fscache) if (cifsi->fscache)
return; return;
cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, cifsi->fscache = fscache_acquire_cookie(tcon->fscache,
&cifs_fscache_inode_object_def, &cifs_fscache_inode_object_def, cifsi);
cifsi); cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", tcon->fscache,
cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", cifsi->fscache);
cifs_sb->tcon->fscache, cifsi->fscache);
} }
void cifs_fscache_release_inode_cookie(struct inode *inode) void cifs_fscache_release_inode_cookie(struct inode *inode)
...@@ -117,7 +117,8 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode) ...@@ -117,7 +117,8 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode)
/* retire the current fscache cache and get a new one */ /* retire the current fscache cache and get a new one */
fscache_relinquish_cookie(cifsi->fscache, 1); fscache_relinquish_cookie(cifsi->fscache, 1);
cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache, cifsi->fscache = fscache_acquire_cookie(
cifs_sb_master_tcon(cifs_sb)->fscache,
&cifs_fscache_inode_object_def, &cifs_fscache_inode_object_def,
cifsi); cifsi);
cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p", cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p",
......
...@@ -52,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral) ...@@ -52,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
/* check if server can support readpages */ /* check if server can support readpages */
if (cifs_sb->tcon->ses->server->maxBuf < if (cifs_sb_master_tcon(cifs_sb)->ses->server->maxBuf <
PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE) PAGE_CACHE_SIZE + MAX_CIFS_HDR_SIZE)
inode->i_data.a_ops = &cifs_addr_ops_smallbuf; inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
else else
...@@ -288,8 +288,8 @@ int cifs_get_file_info_unix(struct file *filp) ...@@ -288,8 +288,8 @@ int cifs_get_file_info_unix(struct file *filp)
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *tcon = cifs_sb->tcon;
struct cifsFileInfo *cfile = filp->private_data; struct cifsFileInfo *cfile = filp->private_data;
struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink);
xid = GetXid(); xid = GetXid();
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data); rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data);
...@@ -313,15 +313,21 @@ int cifs_get_inode_info_unix(struct inode **pinode, ...@@ -313,15 +313,21 @@ int cifs_get_inode_info_unix(struct inode **pinode,
FILE_UNIX_BASIC_INFO find_data; FILE_UNIX_BASIC_INFO find_data;
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
tcon = cifs_sb->tcon;
cFYI(1, "Getting info on %s", full_path); cFYI(1, "Getting info on %s", full_path);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
/* could have done a find first instead but this returns more info */ /* could have done a find first instead but this returns more info */
rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data, rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
if (!rc) { if (!rc) {
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb); cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
...@@ -332,6 +338,13 @@ int cifs_get_inode_info_unix(struct inode **pinode, ...@@ -332,6 +338,13 @@ int cifs_get_inode_info_unix(struct inode **pinode,
return rc; return rc;
} }
/* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
int tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid);
if (tmprc)
cFYI(1, "CIFSCheckMFSymlink: %d", tmprc);
}
if (*pinode == NULL) { if (*pinode == NULL) {
/* get new inode */ /* get new inode */
cifs_fill_uniqueid(sb, &fattr); cifs_fill_uniqueid(sb, &fattr);
...@@ -353,7 +366,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -353,7 +366,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
int rc; int rc;
int oplock = 0; int oplock = 0;
__u16 netfid; __u16 netfid;
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct tcon_link *tlink;
struct cifsTconInfo *tcon;
char buf[24]; char buf[24];
unsigned int bytes_read; unsigned int bytes_read;
char *pbuf; char *pbuf;
...@@ -372,7 +386,12 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -372,7 +386,12 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
return -EINVAL; /* EOPNOTSUPP? */ return -EINVAL; /* EOPNOTSUPP? */
} }
rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ, tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, GENERIC_READ,
CREATE_NOT_DIR, &netfid, &oplock, NULL, CREATE_NOT_DIR, &netfid, &oplock, NULL,
cifs_sb->local_nls, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
...@@ -380,7 +399,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -380,7 +399,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
if (rc == 0) { if (rc == 0) {
int buf_type = CIFS_NO_BUFFER; int buf_type = CIFS_NO_BUFFER;
/* Read header */ /* Read header */
rc = CIFSSMBRead(xid, pTcon, netfid, rc = CIFSSMBRead(xid, tcon, netfid,
24 /* length */, 0 /* offset */, 24 /* length */, 0 /* offset */,
&bytes_read, &pbuf, &buf_type); &bytes_read, &pbuf, &buf_type);
if ((rc == 0) && (bytes_read >= 8)) { if ((rc == 0) && (bytes_read >= 8)) {
...@@ -422,8 +441,9 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -422,8 +441,9 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
rc = -EOPNOTSUPP; /* or some unknown SFU type */ rc = -EOPNOTSUPP; /* or some unknown SFU type */
} }
CIFSSMBClose(xid, pTcon, netfid); CIFSSMBClose(xid, tcon, netfid);
} }
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -441,11 +461,19 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -441,11 +461,19 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
ssize_t rc; ssize_t rc;
char ea_value[4]; char ea_value[4];
__u32 mode; __u32 mode;
struct tcon_link *tlink;
struct cifsTconInfo *tcon;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
rc = CIFSSMBQAllEAs(xid, cifs_sb->tcon, path, "SETFILEBITS", rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS",
ea_value, 4 /* size of buf */, cifs_sb->local_nls, ea_value, 4 /* size of buf */, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
if (rc < 0) if (rc < 0)
return (int)rc; return (int)rc;
else if (rc > 3) { else if (rc > 3) {
...@@ -468,6 +496,8 @@ static void ...@@ -468,6 +496,8 @@ static void
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
struct cifs_sb_info *cifs_sb, bool adjust_tz) struct cifs_sb_info *cifs_sb, bool adjust_tz)
{ {
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
memset(fattr, 0, sizeof(*fattr)); memset(fattr, 0, sizeof(*fattr));
fattr->cf_cifsattrs = le32_to_cpu(info->Attributes); fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
if (info->DeletePending) if (info->DeletePending)
...@@ -482,8 +512,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, ...@@ -482,8 +512,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime); fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
if (adjust_tz) { if (adjust_tz) {
fattr->cf_ctime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
fattr->cf_mtime.tv_sec += cifs_sb->tcon->ses->server->timeAdj; fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
} }
fattr->cf_eof = le64_to_cpu(info->EndOfFile); fattr->cf_eof = le64_to_cpu(info->EndOfFile);
...@@ -515,8 +545,8 @@ int cifs_get_file_info(struct file *filp) ...@@ -515,8 +545,8 @@ int cifs_get_file_info(struct file *filp)
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *tcon = cifs_sb->tcon;
struct cifsFileInfo *cfile = filp->private_data; struct cifsFileInfo *cfile = filp->private_data;
struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink);
xid = GetXid(); xid = GetXid();
rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data); rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data);
...@@ -554,26 +584,33 @@ int cifs_get_inode_info(struct inode **pinode, ...@@ -554,26 +584,33 @@ int cifs_get_inode_info(struct inode **pinode,
{ {
int rc = 0, tmprc; int rc = 0, tmprc;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct tcon_link *tlink;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
char *buf = NULL; char *buf = NULL;
bool adjustTZ = false; bool adjustTZ = false;
struct cifs_fattr fattr; struct cifs_fattr fattr;
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
cFYI(1, "Getting info on %s", full_path); cFYI(1, "Getting info on %s", full_path);
if ((pfindData == NULL) && (*pinode != NULL)) { if ((pfindData == NULL) && (*pinode != NULL)) {
if (CIFS_I(*pinode)->clientCanCacheRead) { if (CIFS_I(*pinode)->clientCanCacheRead) {
cFYI(1, "No need to revalidate cached inode sizes"); cFYI(1, "No need to revalidate cached inode sizes");
return rc; goto cgii_exit;
} }
} }
/* if file info not passed in then get it from server */ /* if file info not passed in then get it from server */
if (pfindData == NULL) { if (pfindData == NULL) {
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
if (buf == NULL) if (buf == NULL) {
return -ENOMEM; rc = -ENOMEM;
goto cgii_exit;
}
pfindData = (FILE_ALL_INFO *)buf; pfindData = (FILE_ALL_INFO *)buf;
/* could do find first instead but this returns more info */ /* could do find first instead but this returns more info */
...@@ -661,6 +698,13 @@ int cifs_get_inode_info(struct inode **pinode, ...@@ -661,6 +698,13 @@ int cifs_get_inode_info(struct inode **pinode,
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
cifs_sfu_mode(&fattr, full_path, cifs_sb, xid); cifs_sfu_mode(&fattr, full_path, cifs_sb, xid);
/* check for Minshall+French symlinks */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid);
if (tmprc)
cFYI(1, "CIFSCheckMFSymlink: %d", tmprc);
}
if (!*pinode) { if (!*pinode) {
*pinode = cifs_iget(sb, &fattr); *pinode = cifs_iget(sb, &fattr);
if (!*pinode) if (!*pinode)
...@@ -671,6 +715,7 @@ int cifs_get_inode_info(struct inode **pinode, ...@@ -671,6 +715,7 @@ int cifs_get_inode_info(struct inode **pinode,
cgii_exit: cgii_exit:
kfree(buf); kfree(buf);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -683,6 +728,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) ...@@ -683,6 +728,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
int pplen = cifs_sb->prepathlen; int pplen = cifs_sb->prepathlen;
int dfsplen; int dfsplen;
char *full_path = NULL; char *full_path = NULL;
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
/* if no prefix path, simply set path to the root of share to "" */ /* if no prefix path, simply set path to the root of share to "" */
if (pplen == 0) { if (pplen == 0) {
...@@ -692,8 +738,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) ...@@ -692,8 +738,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
return full_path; return full_path;
} }
if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS)) if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1); dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
else else
dfsplen = 0; dfsplen = 0;
...@@ -702,7 +748,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb) ...@@ -702,7 +748,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
return full_path; return full_path;
if (dfsplen) { if (dfsplen) {
strncpy(full_path, cifs_sb->tcon->treeName, dfsplen); strncpy(full_path, tcon->treeName, dfsplen);
/* switch slash direction in prepath depending on whether /* switch slash direction in prepath depending on whether
* windows or posix style path names * windows or posix style path names
*/ */
...@@ -818,18 +864,18 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr) ...@@ -818,18 +864,18 @@ cifs_iget(struct super_block *sb, struct cifs_fattr *fattr)
struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
{ {
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct inode *inode = NULL; struct inode *inode = NULL;
long rc; long rc;
char *full_path; char *full_path;
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
cifs_sb = CIFS_SB(sb);
full_path = cifs_build_path_to_root(cifs_sb); full_path = cifs_build_path_to_root(cifs_sb);
if (full_path == NULL) if (full_path == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
xid = GetXid(); xid = GetXid();
if (cifs_sb->tcon->unix_ext) if (tcon->unix_ext)
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
else else
rc = cifs_get_inode_info(&inode, full_path, NULL, sb, rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
...@@ -840,10 +886,10 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino) ...@@ -840,10 +886,10 @@ struct inode *cifs_root_iget(struct super_block *sb, unsigned long ino)
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
/* populate tcon->resource_id */ /* populate tcon->resource_id */
cifs_sb->tcon->resource_id = CIFS_I(inode)->uniqueid; tcon->resource_id = CIFS_I(inode)->uniqueid;
#endif #endif
if (rc && cifs_sb->tcon->ipc) { if (rc && tcon->ipc) {
cFYI(1, "ipc connection - fake read inode"); cFYI(1, "ipc connection - fake read inode");
inode->i_mode |= S_IFDIR; inode->i_mode |= S_IFDIR;
inode->i_nlink = 2; inode->i_nlink = 2;
...@@ -879,7 +925,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, ...@@ -879,7 +925,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct tcon_link *tlink = NULL;
struct cifsTconInfo *pTcon;
FILE_BASIC_INFO info_buf; FILE_BASIC_INFO info_buf;
if (attrs == NULL) if (attrs == NULL)
...@@ -918,13 +965,22 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, ...@@ -918,13 +965,22 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
/* /*
* If the file is already open for write, just use that fileid * If the file is already open for write, just use that fileid
*/ */
open_file = find_writable_file(cifsInode); open_file = find_writable_file(cifsInode, true);
if (open_file) { if (open_file) {
netfid = open_file->netfid; netfid = open_file->netfid;
netpid = open_file->pid; netpid = open_file->pid;
pTcon = tlink_tcon(open_file->tlink);
goto set_via_filehandle; goto set_via_filehandle;
} }
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
tlink = NULL;
goto out;
}
pTcon = tlink_tcon(tlink);
/* /*
* NT4 apparently returns success on this call, but it doesn't * NT4 apparently returns success on this call, but it doesn't
* really work. * really work.
...@@ -968,6 +1024,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid, ...@@ -968,6 +1024,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
else else
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
out: out:
if (tlink != NULL)
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -985,10 +1043,16 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) ...@@ -985,10 +1043,16 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *tcon = cifs_sb->tcon; struct tcon_link *tlink;
struct cifsTconInfo *tcon;
__u32 dosattr, origattr; __u32 dosattr, origattr;
FILE_BASIC_INFO *info_buf = NULL; FILE_BASIC_INFO *info_buf = NULL;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
&netfid, &oplock, NULL, cifs_sb->local_nls, &netfid, &oplock, NULL, cifs_sb->local_nls,
...@@ -1057,6 +1121,7 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid) ...@@ -1057,6 +1121,7 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
CIFSSMBClose(xid, tcon, netfid); CIFSSMBClose(xid, tcon, netfid);
out: out:
kfree(info_buf); kfree(info_buf);
cifs_put_tlink(tlink);
return rc; return rc;
/* /*
...@@ -1096,12 +1161,18 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1096,12 +1161,18 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
struct cifsInodeInfo *cifs_inode; struct cifsInodeInfo *cifs_inode;
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
struct cifs_sb_info *cifs_sb = CIFS_SB(sb); struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
struct cifsTconInfo *tcon = cifs_sb->tcon; struct tcon_link *tlink;
struct cifsTconInfo *tcon;
struct iattr *attrs = NULL; struct iattr *attrs = NULL;
__u32 dosattr = 0, origattr = 0; __u32 dosattr = 0, origattr = 0;
cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry); cFYI(1, "cifs_unlink, dir=0x%p, dentry=0x%p", dir, dentry);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
xid = GetXid(); xid = GetXid();
/* Unlink can be called from rename so we can not take the /* Unlink can be called from rename so we can not take the
...@@ -1109,8 +1180,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1109,8 +1180,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
full_path = build_path_from_dentry(dentry); full_path = build_path_from_dentry(dentry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto unlink_out;
return rc;
} }
if ((tcon->ses->capabilities & CAP_UNIX) && if ((tcon->ses->capabilities & CAP_UNIX) &&
...@@ -1176,10 +1246,11 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1176,10 +1246,11 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
dir->i_ctime = dir->i_mtime = current_fs_time(sb); dir->i_ctime = dir->i_mtime = current_fs_time(sb);
cifs_inode = CIFS_I(dir); cifs_inode = CIFS_I(dir);
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */ CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
unlink_out:
kfree(full_path); kfree(full_path);
kfree(attrs); kfree(attrs);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -1188,6 +1259,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1188,6 +1259,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
int rc = 0, tmprc; int rc = 0, tmprc;
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
char *full_path = NULL; char *full_path = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
...@@ -1195,16 +1267,18 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1195,16 +1267,18 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode); cFYI(1, "In cifs_mkdir, mode = 0x%x inode = 0x%p", mode, inode);
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto mkdir_out;
return rc;
} }
if ((pTcon->ses->capabilities & CAP_UNIX) && if ((pTcon->ses->capabilities & CAP_UNIX) &&
...@@ -1362,6 +1436,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode) ...@@ -1362,6 +1436,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
mkdir_out: mkdir_out:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -1370,6 +1445,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) ...@@ -1370,6 +1445,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
int rc = 0; int rc = 0;
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
char *full_path = NULL; char *full_path = NULL;
struct cifsInodeInfo *cifsInode; struct cifsInodeInfo *cifsInode;
...@@ -1378,18 +1454,23 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) ...@@ -1378,18 +1454,23 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb);
pTcon = cifs_sb->tcon;
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto rmdir_exit;
return rc; }
cifs_sb = CIFS_SB(inode->i_sb);
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
goto rmdir_exit;
} }
pTcon = tlink_tcon(tlink);
rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls, rc = CIFSSMBRmDir(xid, pTcon, full_path, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
if (!rc) { if (!rc) {
drop_nlink(inode); drop_nlink(inode);
...@@ -1410,6 +1491,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry) ...@@ -1410,6 +1491,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime = direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
current_fs_time(inode->i_sb); current_fs_time(inode->i_sb);
rmdir_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
...@@ -1420,10 +1502,16 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, ...@@ -1420,10 +1502,16 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
struct dentry *to_dentry, const char *toPath) struct dentry *to_dentry, const char *toPath)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(from_dentry->d_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct tcon_link *tlink;
struct cifsTconInfo *pTcon;
__u16 srcfid; __u16 srcfid;
int oplock, rc; int oplock, rc;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
/* try path-based rename first */ /* try path-based rename first */
rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls, rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
...@@ -1435,11 +1523,11 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, ...@@ -1435,11 +1523,11 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
* rename by filehandle to various Windows servers. * rename by filehandle to various Windows servers.
*/ */
if (rc == 0 || rc != -ETXTBSY) if (rc == 0 || rc != -ETXTBSY)
return rc; goto do_rename_exit;
/* open-file renames don't work across directories */ /* open-file renames don't work across directories */
if (to_dentry->d_parent != from_dentry->d_parent) if (to_dentry->d_parent != from_dentry->d_parent)
return rc; goto do_rename_exit;
/* open the file to be renamed -- we need DELETE perms */ /* open the file to be renamed -- we need DELETE perms */
rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE, rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
...@@ -1455,7 +1543,8 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath, ...@@ -1455,7 +1543,8 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
CIFSSMBClose(xid, pTcon, srcfid); CIFSSMBClose(xid, pTcon, srcfid);
} }
do_rename_exit:
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -1465,13 +1554,17 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, ...@@ -1465,13 +1554,17 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
char *fromName = NULL; char *fromName = NULL;
char *toName = NULL; char *toName = NULL;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *tcon; struct cifsTconInfo *tcon;
FILE_UNIX_BASIC_INFO *info_buf_source = NULL; FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
FILE_UNIX_BASIC_INFO *info_buf_target; FILE_UNIX_BASIC_INFO *info_buf_target;
int xid, rc, tmprc; int xid, rc, tmprc;
cifs_sb = CIFS_SB(source_dir->i_sb); cifs_sb = CIFS_SB(source_dir->i_sb);
tcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
tcon = tlink_tcon(tlink);
xid = GetXid(); xid = GetXid();
...@@ -1547,6 +1640,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry, ...@@ -1547,6 +1640,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
kfree(fromName); kfree(fromName);
kfree(toName); kfree(toName);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -1599,11 +1693,12 @@ int cifs_revalidate_file(struct file *filp) ...@@ -1599,11 +1693,12 @@ int cifs_revalidate_file(struct file *filp)
{ {
int rc = 0; int rc = 0;
struct inode *inode = filp->f_path.dentry->d_inode; struct inode *inode = filp->f_path.dentry->d_inode;
struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data;
if (!cifs_inode_needs_reval(inode)) if (!cifs_inode_needs_reval(inode))
goto check_inval; goto check_inval;
if (CIFS_SB(inode->i_sb)->tcon->unix_ext) if (tlink_tcon(cfile->tlink)->unix_ext)
rc = cifs_get_file_info_unix(filp); rc = cifs_get_file_info_unix(filp);
else else
rc = cifs_get_file_info(filp); rc = cifs_get_file_info(filp);
...@@ -1644,7 +1739,7 @@ int cifs_revalidate_dentry(struct dentry *dentry) ...@@ -1644,7 +1739,7 @@ int cifs_revalidate_dentry(struct dentry *dentry)
"jiffies %ld", full_path, inode, inode->i_count.counter, "jiffies %ld", full_path, inode, inode->i_count.counter,
dentry, dentry->d_time, jiffies); dentry, dentry->d_time, jiffies);
if (CIFS_SB(sb)->tcon->unix_ext) if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid); rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
else else
rc = cifs_get_inode_info(&inode, full_path, NULL, sb, rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
...@@ -1662,11 +1757,27 @@ int cifs_revalidate_dentry(struct dentry *dentry) ...@@ -1662,11 +1757,27 @@ int cifs_revalidate_dentry(struct dentry *dentry)
int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
struct kstat *stat) struct kstat *stat)
{ {
struct cifs_sb_info *cifs_sb = CIFS_SB(dentry->d_sb);
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
int err = cifs_revalidate_dentry(dentry); int err = cifs_revalidate_dentry(dentry);
if (!err) { if (!err) {
generic_fillattr(dentry->d_inode, stat); generic_fillattr(dentry->d_inode, stat);
stat->blksize = CIFS_MAX_MSGSIZE; stat->blksize = CIFS_MAX_MSGSIZE;
stat->ino = CIFS_I(dentry->d_inode)->uniqueid; stat->ino = CIFS_I(dentry->d_inode)->uniqueid;
/*
* If on a multiuser mount without unix extensions, and the
* admin hasn't overridden them, set the ownership to the
* fsuid/fsgid of the current process.
*/
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
!tcon->unix_ext) {
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID))
stat->uid = current_fsuid();
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID))
stat->gid = current_fsgid();
}
} }
return err; return err;
} }
...@@ -1708,7 +1819,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1708,7 +1819,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct tcon_link *tlink = NULL;
struct cifsTconInfo *pTcon = NULL;
/* /*
* To avoid spurious oplock breaks from server, in the case of * To avoid spurious oplock breaks from server, in the case of
...@@ -1719,10 +1831,11 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1719,10 +1831,11 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
* writebehind data than the SMB timeout for the SetPathInfo * writebehind data than the SMB timeout for the SetPathInfo
* request would allow * request would allow
*/ */
open_file = find_writable_file(cifsInode); open_file = find_writable_file(cifsInode, true);
if (open_file) { if (open_file) {
__u16 nfid = open_file->netfid; __u16 nfid = open_file->netfid;
__u32 npid = open_file->pid; __u32 npid = open_file->pid;
pTcon = tlink_tcon(open_file->tlink);
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid, rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
npid, false); npid, false);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
...@@ -1737,6 +1850,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1737,6 +1850,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
rc = -EINVAL; rc = -EINVAL;
if (rc != 0) { if (rc != 0) {
if (pTcon == NULL) {
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
}
/* Set file size by pathname rather than by handle /* Set file size by pathname rather than by handle
either because no valid, writeable file handle for either because no valid, writeable file handle for
it was found or because there was an error setting it was found or because there was an error setting
...@@ -1766,6 +1886,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs, ...@@ -1766,6 +1886,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
CIFSSMBClose(xid, pTcon, netfid); CIFSSMBClose(xid, pTcon, netfid);
} }
} }
if (tlink)
cifs_put_tlink(tlink);
} }
if (rc == 0) { if (rc == 0) {
...@@ -1786,7 +1908,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) ...@@ -1786,7 +1908,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
struct inode *inode = direntry->d_inode; struct inode *inode = direntry->d_inode;
struct cifsInodeInfo *cifsInode = CIFS_I(inode); struct cifsInodeInfo *cifsInode = CIFS_I(inode);
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct tcon_link *tlink;
struct cifsTconInfo *pTcon;
struct cifs_unix_set_info_args *args = NULL; struct cifs_unix_set_info_args *args = NULL;
struct cifsFileInfo *open_file; struct cifsFileInfo *open_file;
...@@ -1873,17 +1996,25 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs) ...@@ -1873,17 +1996,25 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
args->ctime = NO_CHANGE_64; args->ctime = NO_CHANGE_64;
args->device = 0; args->device = 0;
open_file = find_writable_file(cifsInode); open_file = find_writable_file(cifsInode, true);
if (open_file) { if (open_file) {
u16 nfid = open_file->netfid; u16 nfid = open_file->netfid;
u32 npid = open_file->pid; u32 npid = open_file->pid;
pTcon = tlink_tcon(open_file->tlink);
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid); rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
cifsFileInfo_put(open_file); cifsFileInfo_put(open_file);
} else { } else {
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
goto out;
}
pTcon = tlink_tcon(tlink);
rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args, rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, args,
cifs_sb->local_nls, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
cifs_put_tlink(tlink);
} }
if (rc) if (rc)
...@@ -2064,7 +2195,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs) ...@@ -2064,7 +2195,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs)
{ {
struct inode *inode = direntry->d_inode; struct inode *inode = direntry->d_inode;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *pTcon = cifs_sb->tcon; struct cifsTconInfo *pTcon = cifs_sb_master_tcon(cifs_sb);
if (pTcon->unix_ext) if (pTcon->unix_ext)
return cifs_setattr_unix(direntry, attrs); return cifs_setattr_unix(direntry, attrs);
......
...@@ -37,11 +37,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -37,11 +37,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
#ifdef CONFIG_CIFS_POSIX #ifdef CONFIG_CIFS_POSIX
struct cifsFileInfo *pSMBFile = filep->private_data;
struct cifsTconInfo *tcon = tlink_tcon(pSMBFile->tlink);
__u64 ExtAttrBits = 0; __u64 ExtAttrBits = 0;
__u64 ExtAttrMask = 0; __u64 ExtAttrMask = 0;
__u64 caps; __u64 caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
struct cifsTconInfo *tcon;
struct cifsFileInfo *pSMBFile = filep->private_data;
#endif /* CONFIG_CIFS_POSIX */ #endif /* CONFIG_CIFS_POSIX */
xid = GetXid(); xid = GetXid();
...@@ -50,17 +50,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -50,17 +50,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
cifs_sb = CIFS_SB(inode->i_sb); cifs_sb = CIFS_SB(inode->i_sb);
#ifdef CONFIG_CIFS_POSIX
tcon = cifs_sb->tcon;
if (tcon)
caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
else {
rc = -EIO;
FreeXid(xid);
return -EIO;
}
#endif /* CONFIG_CIFS_POSIX */
switch (command) { switch (command) {
case CIFS_IOC_CHECKUMOUNT: case CIFS_IOC_CHECKUMOUNT:
cFYI(1, "User unmount attempted"); cFYI(1, "User unmount attempted");
......
...@@ -28,6 +28,296 @@ ...@@ -28,6 +28,296 @@
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "md5.h"
#define CIFS_MF_SYMLINK_LEN_OFFSET (4+1)
#define CIFS_MF_SYMLINK_MD5_OFFSET (CIFS_MF_SYMLINK_LEN_OFFSET+(4+1))
#define CIFS_MF_SYMLINK_LINK_OFFSET (CIFS_MF_SYMLINK_MD5_OFFSET+(32+1))
#define CIFS_MF_SYMLINK_LINK_MAXLEN (1024)
#define CIFS_MF_SYMLINK_FILE_SIZE \
(CIFS_MF_SYMLINK_LINK_OFFSET + CIFS_MF_SYMLINK_LINK_MAXLEN)
#define CIFS_MF_SYMLINK_LEN_FORMAT "XSym\n%04u\n"
#define CIFS_MF_SYMLINK_MD5_FORMAT \
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n"
#define CIFS_MF_SYMLINK_MD5_ARGS(md5_hash) \
md5_hash[0], md5_hash[1], md5_hash[2], md5_hash[3], \
md5_hash[4], md5_hash[5], md5_hash[6], md5_hash[7], \
md5_hash[8], md5_hash[9], md5_hash[10], md5_hash[11],\
md5_hash[12], md5_hash[13], md5_hash[14], md5_hash[15]
static int
CIFSParseMFSymlink(const u8 *buf,
unsigned int buf_len,
unsigned int *_link_len,
char **_link_str)
{
int rc;
unsigned int link_len;
const char *md5_str1;
const char *link_str;
struct MD5Context md5_ctx;
u8 md5_hash[16];
char md5_str2[34];
if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
return -EINVAL;
md5_str1 = (const char *)&buf[CIFS_MF_SYMLINK_MD5_OFFSET];
link_str = (const char *)&buf[CIFS_MF_SYMLINK_LINK_OFFSET];
rc = sscanf(buf, CIFS_MF_SYMLINK_LEN_FORMAT, &link_len);
if (rc != 1)
return -EINVAL;
cifs_MD5_init(&md5_ctx);
cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len);
cifs_MD5_final(md5_hash, &md5_ctx);
snprintf(md5_str2, sizeof(md5_str2),
CIFS_MF_SYMLINK_MD5_FORMAT,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
if (strncmp(md5_str1, md5_str2, 17) != 0)
return -EINVAL;
if (_link_str) {
*_link_str = kstrndup(link_str, link_len, GFP_KERNEL);
if (!*_link_str)
return -ENOMEM;
}
*_link_len = link_len;
return 0;
}
static int
CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)
{
unsigned int link_len;
unsigned int ofs;
struct MD5Context md5_ctx;
u8 md5_hash[16];
if (buf_len != CIFS_MF_SYMLINK_FILE_SIZE)
return -EINVAL;
link_len = strlen(link_str);
if (link_len > CIFS_MF_SYMLINK_LINK_MAXLEN)
return -ENAMETOOLONG;
cifs_MD5_init(&md5_ctx);
cifs_MD5_update(&md5_ctx, (const u8 *)link_str, link_len);
cifs_MD5_final(md5_hash, &md5_ctx);
snprintf(buf, buf_len,
CIFS_MF_SYMLINK_LEN_FORMAT CIFS_MF_SYMLINK_MD5_FORMAT,
link_len,
CIFS_MF_SYMLINK_MD5_ARGS(md5_hash));
ofs = CIFS_MF_SYMLINK_LINK_OFFSET;
memcpy(buf + ofs, link_str, link_len);
ofs += link_len;
if (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
buf[ofs] = '\n';
ofs++;
}
while (ofs < CIFS_MF_SYMLINK_FILE_SIZE) {
buf[ofs] = ' ';
ofs++;
}
return 0;
}
static int
CIFSCreateMFSymLink(const int xid, struct cifsTconInfo *tcon,
const char *fromName, const char *toName,
const struct nls_table *nls_codepage, int remap)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
u8 *buf;
unsigned int bytes_written = 0;
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
rc = CIFSFormatMFSymlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName);
if (rc != 0) {
kfree(buf);
return rc;
}
rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE,
CREATE_NOT_DIR, &netfid, &oplock, NULL,
nls_codepage, remap);
if (rc != 0) {
kfree(buf);
return rc;
}
rc = CIFSSMBWrite(xid, tcon, netfid,
CIFS_MF_SYMLINK_FILE_SIZE /* length */,
0 /* offset */,
&bytes_written, buf, NULL, 0);
CIFSSMBClose(xid, tcon, netfid);
kfree(buf);
if (rc != 0)
return rc;
if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE)
return -EIO;
return 0;
}
static int
CIFSQueryMFSymLink(const int xid, struct cifsTconInfo *tcon,
const unsigned char *searchName, char **symlinkinfo,
const struct nls_table *nls_codepage, int remap)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
u8 *buf;
char *pbuf;
unsigned int bytes_read = 0;
int buf_type = CIFS_NO_BUFFER;
unsigned int link_len = 0;
FILE_ALL_INFO file_info;
rc = CIFSSMBOpen(xid, tcon, searchName, FILE_OPEN, GENERIC_READ,
CREATE_NOT_DIR, &netfid, &oplock, &file_info,
nls_codepage, remap);
if (rc != 0)
return rc;
if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
CIFSSMBClose(xid, tcon, netfid);
/* it's not a symlink */
return -EINVAL;
}
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf)
return -ENOMEM;
pbuf = buf;
rc = CIFSSMBRead(xid, tcon, netfid,
CIFS_MF_SYMLINK_FILE_SIZE /* length */,
0 /* offset */,
&bytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, tcon, netfid);
if (rc != 0) {
kfree(buf);
return rc;
}
rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, symlinkinfo);
kfree(buf);
if (rc != 0)
return rc;
return 0;
}
bool
CIFSCouldBeMFSymlink(const struct cifs_fattr *fattr)
{
if (!(fattr->cf_mode & S_IFREG))
/* it's not a symlink */
return false;
if (fattr->cf_eof != CIFS_MF_SYMLINK_FILE_SIZE)
/* it's not a symlink */
return false;
return true;
}
int
CIFSCheckMFSymlink(struct cifs_fattr *fattr,
const unsigned char *path,
struct cifs_sb_info *cifs_sb, int xid)
{
int rc;
int oplock = 0;
__u16 netfid = 0;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon;
u8 *buf;
char *pbuf;
unsigned int bytes_read = 0;
int buf_type = CIFS_NO_BUFFER;
unsigned int link_len = 0;
FILE_ALL_INFO file_info;
if (!CIFSCouldBeMFSymlink(fattr))
/* it's not a symlink */
return 0;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
rc = CIFSSMBOpen(xid, pTcon, path, FILE_OPEN, GENERIC_READ,
CREATE_NOT_DIR, &netfid, &oplock, &file_info,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if (rc != 0)
goto out;
if (file_info.EndOfFile != CIFS_MF_SYMLINK_FILE_SIZE) {
CIFSSMBClose(xid, pTcon, netfid);
/* it's not a symlink */
goto out;
}
buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);
if (!buf) {
rc = -ENOMEM;
goto out;
}
pbuf = buf;
rc = CIFSSMBRead(xid, pTcon, netfid,
CIFS_MF_SYMLINK_FILE_SIZE /* length */,
0 /* offset */,
&bytes_read, &pbuf, &buf_type);
CIFSSMBClose(xid, pTcon, netfid);
if (rc != 0) {
kfree(buf);
goto out;
}
rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL);
kfree(buf);
if (rc == -EINVAL) {
/* it's not a symlink */
rc = 0;
goto out;
}
if (rc != 0)
goto out;
/* it is a symlink */
fattr->cf_eof = link_len;
fattr->cf_mode &= ~S_IFMT;
fattr->cf_mode |= S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
fattr->cf_dtype = DT_LNK;
out:
cifs_put_tlink(tlink);
return rc;
}
int int
cifs_hardlink(struct dentry *old_file, struct inode *inode, cifs_hardlink(struct dentry *old_file, struct inode *inode,
...@@ -37,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, ...@@ -37,17 +327,17 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
int xid; int xid;
char *fromName = NULL; char *fromName = NULL;
char *toName = NULL; char *toName = NULL;
struct cifs_sb_info *cifs_sb_target; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct cifsInodeInfo *cifsInode; struct cifsInodeInfo *cifsInode;
xid = GetXid(); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
cifs_sb_target = CIFS_SB(inode->i_sb); return PTR_ERR(tlink);
pTcon = cifs_sb_target->tcon; pTcon = tlink_tcon(tlink);
/* No need to check for cross device links since server will do that xid = GetXid();
BB note DFS case in future though (when we may have to check) */
fromName = build_path_from_dentry(old_file); fromName = build_path_from_dentry(old_file);
toName = build_path_from_dentry(direntry); toName = build_path_from_dentry(direntry);
...@@ -56,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, ...@@ -56,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
goto cifs_hl_exit; goto cifs_hl_exit;
} }
/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
if (pTcon->unix_ext) if (pTcon->unix_ext)
rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName, rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
cifs_sb_target->local_nls, cifs_sb->local_nls,
cifs_sb_target->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
else { else {
rc = CIFSCreateHardLink(xid, pTcon, fromName, toName, rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
cifs_sb_target->local_nls, cifs_sb->local_nls,
cifs_sb_target->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
if ((rc == -EIO) || (rc == -EINVAL)) if ((rc == -EIO) || (rc == -EINVAL))
rc = -EOPNOTSUPP; rc = -EOPNOTSUPP;
...@@ -101,6 +390,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode, ...@@ -101,6 +390,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
kfree(fromName); kfree(fromName);
kfree(toName); kfree(toName);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -113,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -113,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
char *full_path = NULL; char *full_path = NULL;
char *target_path = NULL; char *target_path = NULL;
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct cifsTconInfo *tcon = cifs_sb->tcon; struct tcon_link *tlink = NULL;
struct cifsTconInfo *tcon;
xid = GetXid(); xid = GetXid();
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
tlink = NULL;
goto out;
}
tcon = tlink_tcon(tlink);
/* /*
* For now, we just handle symlinks with unix extensions enabled. * For now, we just handle symlinks with unix extensions enabled.
* Eventually we should handle NTFS reparse points, and MacOS * Eventually we should handle NTFS reparse points, and MacOS
...@@ -130,7 +429,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -130,7 +429,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
* but there doesn't seem to be any harm in allowing the client to * but there doesn't seem to be any harm in allowing the client to
* read them. * read them.
*/ */
if (!(tcon->ses->capabilities & CAP_UNIX)) { if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
&& !(tcon->ses->capabilities & CAP_UNIX)) {
rc = -EACCES; rc = -EACCES;
goto out; goto out;
} }
...@@ -141,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -141,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
cFYI(1, "Full path: %s inode = 0x%p", full_path, inode); cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
rc = -EACCES;
/*
* First try Minshall+French Symlinks, if configured
* and fallback to UNIX Extensions Symlinks.
*/
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
rc = CIFSQueryMFSymLink(xid, tcon, full_path, &target_path,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
if ((rc != 0) && (tcon->ses->capabilities & CAP_UNIX))
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
cifs_sb->local_nls); cifs_sb->local_nls);
kfree(full_path); kfree(full_path);
out: out:
if (rc != 0) { if (rc != 0) {
...@@ -151,6 +464,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd) ...@@ -151,6 +464,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
} }
FreeXid(xid); FreeXid(xid);
if (tlink)
cifs_put_tlink(tlink);
nd_set_link(nd, target_path); nd_set_link(nd, target_path);
return NULL; return NULL;
} }
...@@ -160,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) ...@@ -160,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
{ {
int rc = -EOPNOTSUPP; int rc = -EOPNOTSUPP;
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
char *full_path = NULL; char *full_path = NULL;
struct inode *newinode = NULL; struct inode *newinode = NULL;
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb); tlink = cifs_sb_tlink(cifs_sb);
pTcon = cifs_sb->tcon; if (IS_ERR(tlink)) {
rc = PTR_ERR(tlink);
goto symlink_exit;
}
pTcon = tlink_tcon(tlink);
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto symlink_exit;
return rc;
} }
cFYI(1, "Full path: %s", full_path); cFYI(1, "Full path: %s", full_path);
cFYI(1, "symname is %s", symname); cFYI(1, "symname is %s", symname);
/* BB what if DFS and this volume is on different share? BB */ /* BB what if DFS and this volume is on different share? BB */
if (pTcon->unix_ext) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS)
rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
else if (pTcon->unix_ext)
rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname, rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,
cifs_sb->local_nls); cifs_sb->local_nls);
/* else /* else
...@@ -208,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname) ...@@ -208,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
d_instantiate(direntry, newinode); d_instantiate(direntry, newinode);
} }
} }
symlink_exit:
kfree(full_path); kfree(full_path);
cifs_put_tlink(tlink);
FreeXid(xid); FreeXid(xid);
return rc; return rc;
} }
......
...@@ -347,7 +347,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , ...@@ -347,7 +347,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
if (current_fsuid() != treeCon->ses->linux_uid) { if (current_fsuid() != treeCon->ses->linux_uid) {
cFYI(1, "Multiuser mode and UID " cFYI(1, "Multiuser mode and UID "
"did not match tcon uid"); "did not match tcon uid");
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) { list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list); ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
if (ses->linux_uid == current_fsuid()) { if (ses->linux_uid == current_fsuid()) {
...@@ -361,7 +361,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , ...@@ -361,7 +361,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
} }
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
} }
} }
} }
...@@ -551,7 +551,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -551,7 +551,7 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
return false; return false;
/* look up tcon based on tid & uid */ /* look up tcon based on tid & uid */
read_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each(tmp, &srv->smb_ses_list) { list_for_each(tmp, &srv->smb_ses_list) {
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
list_for_each(tmp1, &ses->tcon_list) { list_for_each(tmp1, &ses->tcon_list) {
...@@ -560,25 +560,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -560,25 +560,15 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
continue; continue;
cifs_stats_inc(&tcon->num_oplock_brks); cifs_stats_inc(&tcon->num_oplock_brks);
read_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
list_for_each(tmp2, &tcon->openFileList) { list_for_each(tmp2, &tcon->openFileList) {
netfile = list_entry(tmp2, struct cifsFileInfo, netfile = list_entry(tmp2, struct cifsFileInfo,
tlist); tlist);
if (pSMB->Fid != netfile->netfid) if (pSMB->Fid != netfile->netfid)
continue; continue;
/*
* don't do anything if file is about to be
* closed anyway.
*/
if (netfile->closePend) {
read_unlock(&GlobalSMBSeslock);
read_unlock(&cifs_tcp_ses_lock);
return true;
}
cFYI(1, "file id match, oplock break"); cFYI(1, "file id match, oplock break");
pCifsInode = CIFS_I(netfile->pInode); pCifsInode = CIFS_I(netfile->dentry->d_inode);
pCifsInode->clientCanCacheAll = false; pCifsInode->clientCanCacheAll = false;
if (pSMB->OplockLevel == 0) if (pSMB->OplockLevel == 0)
pCifsInode->clientCanCacheRead = false; pCifsInode->clientCanCacheRead = false;
...@@ -594,17 +584,17 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) ...@@ -594,17 +584,17 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
cifs_oplock_break_get(netfile); cifs_oplock_break_get(netfile);
netfile->oplock_break_cancelled = false; netfile->oplock_break_cancelled = false;
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
read_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cFYI(1, "No matching file for oplock break"); cFYI(1, "No matching file for oplock break");
return true; return true;
} }
} }
read_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
cFYI(1, "Can not process oplock break for non-existent connection"); cFYI(1, "Can not process oplock break for non-existent connection");
return true; return true;
} }
...@@ -729,6 +719,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb) ...@@ -729,6 +719,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
"properly. Hardlinks will not be recognized on this " "properly. Hardlinks will not be recognized on this "
"mount. Consider mounting with the \"noserverino\" " "mount. Consider mounting with the \"noserverino\" "
"option to silence this message.", "option to silence this message.",
cifs_sb->tcon->treeName); cifs_sb_master_tcon(cifs_sb)->treeName);
} }
} }
...@@ -61,6 +61,21 @@ ...@@ -61,6 +61,21 @@
#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000 #define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
#define NTLMSSP_NEGOTIATE_56 0x80000000 #define NTLMSSP_NEGOTIATE_56 0x80000000
/* Define AV Pair Field IDs */
enum av_field_type {
NTLMSSP_AV_EOL = 0,
NTLMSSP_AV_NB_COMPUTER_NAME,
NTLMSSP_AV_NB_DOMAIN_NAME,
NTLMSSP_AV_DNS_COMPUTER_NAME,
NTLMSSP_AV_DNS_DOMAIN_NAME,
NTLMSSP_AV_DNS_TREE_NAME,
NTLMSSP_AV_FLAGS,
NTLMSSP_AV_TIMESTAMP,
NTLMSSP_AV_RESTRICTION,
NTLMSSP_AV_TARGET_NAME,
NTLMSSP_AV_CHANNEL_BINDINGS
};
/* Although typedefs are not commonly used for structure definitions */ /* Although typedefs are not commonly used for structure definitions */
/* in the Linux kernel, in this particular case they are useful */ /* in the Linux kernel, in this particular case they are useful */
/* to more closely match the standards document for NTLMSSP from */ /* to more closely match the standards document for NTLMSSP from */
......
...@@ -102,7 +102,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name, ...@@ -102,7 +102,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
return NULL; return NULL;
} }
if (CIFS_SB(sb)->tcon->nocase) if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
dentry->d_op = &cifs_ci_dentry_ops; dentry->d_op = &cifs_ci_dentry_ops;
else else
dentry->d_op = &cifs_dentry_ops; dentry->d_op = &cifs_dentry_ops;
...@@ -171,7 +171,7 @@ static void ...@@ -171,7 +171,7 @@ static void
cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info, cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
int offset = cifs_sb->tcon->ses->server->timeAdj; int offset = cifs_sb_master_tcon(cifs_sb)->ses->server->timeAdj;
memset(fattr, 0, sizeof(*fattr)); memset(fattr, 0, sizeof(*fattr));
fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate, fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
...@@ -199,7 +199,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, ...@@ -199,7 +199,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
int len; int len;
int oplock = 0; int oplock = 0;
int rc; int rc;
struct cifsTconInfo *ptcon = cifs_sb->tcon; struct cifsTconInfo *ptcon = cifs_sb_tcon(cifs_sb);
char *tmpbuffer; char *tmpbuffer;
rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ, rc = CIFSSMBOpen(xid, ptcon, full_path, FILE_OPEN, GENERIC_READ,
...@@ -223,34 +223,35 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb, ...@@ -223,34 +223,35 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
static int initiate_cifs_search(const int xid, struct file *file) static int initiate_cifs_search(const int xid, struct file *file)
{ {
int rc = 0; int rc = 0;
char *full_path; char *full_path = NULL;
struct cifsFileInfo *cifsFile; struct cifsFileInfo *cifsFile;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
if (file->private_data == NULL) { tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
if (file->private_data == NULL)
file->private_data = file->private_data =
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (file->private_data == NULL) {
rc = -ENOMEM;
goto error_exit;
} }
if (file->private_data == NULL)
return -ENOMEM;
cifsFile = file->private_data; cifsFile = file->private_data;
cifsFile->invalidHandle = true; cifsFile->invalidHandle = true;
cifsFile->srch_inf.endOfSearch = false; cifsFile->srch_inf.endOfSearch = false;
cifsFile->tlink = cifs_get_tlink(tlink);
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
if (cifs_sb == NULL)
return -EINVAL;
pTcon = cifs_sb->tcon;
if (pTcon == NULL)
return -EINVAL;
full_path = build_path_from_dentry(file->f_path.dentry); full_path = build_path_from_dentry(file->f_path.dentry);
if (full_path == NULL) {
if (full_path == NULL) rc = -ENOMEM;
return -ENOMEM; goto error_exit;
}
cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos); cFYI(1, "Full path: %s start at: %lld", full_path, file->f_pos);
...@@ -283,7 +284,9 @@ static int initiate_cifs_search(const int xid, struct file *file) ...@@ -283,7 +284,9 @@ static int initiate_cifs_search(const int xid, struct file *file)
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM; cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
goto ffirst_retry; goto ffirst_retry;
} }
error_exit:
kfree(full_path); kfree(full_path);
cifs_put_tlink(tlink);
return rc; return rc;
} }
...@@ -525,14 +528,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon, ...@@ -525,14 +528,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
(index_to_find < first_entry_in_buffer)) { (index_to_find < first_entry_in_buffer)) {
/* close and restart search */ /* close and restart search */
cFYI(1, "search backing up - close and restart search"); cFYI(1, "search backing up - close and restart search");
write_lock(&GlobalSMBSeslock); spin_lock(&cifs_file_list_lock);
if (!cifsFile->srch_inf.endOfSearch && if (!cifsFile->srch_inf.endOfSearch &&
!cifsFile->invalidHandle) { !cifsFile->invalidHandle) {
cifsFile->invalidHandle = true; cifsFile->invalidHandle = true;
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
CIFSFindClose(xid, pTcon, cifsFile->netfid); CIFSFindClose(xid, pTcon, cifsFile->netfid);
} else } else
write_unlock(&GlobalSMBSeslock); spin_unlock(&cifs_file_list_lock);
if (cifsFile->srch_inf.ntwrk_buf_start) { if (cifsFile->srch_inf.ntwrk_buf_start) {
cFYI(1, "freeing SMB ff cache buf on search rewind"); cFYI(1, "freeing SMB ff cache buf on search rewind");
if (cifsFile->srch_inf.smallBuf) if (cifsFile->srch_inf.smallBuf)
...@@ -738,6 +741,15 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir, ...@@ -738,6 +741,15 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
cifs_autodisable_serverino(cifs_sb); cifs_autodisable_serverino(cifs_sb);
} }
if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) &&
CIFSCouldBeMFSymlink(&fattr))
/*
* trying to get the type and mode can be slow,
* so just call those regular files for now, and mark
* for reval
*/
fattr.cf_flags |= CIFS_FATTR_NEED_REVAL;
ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid); ino = cifs_uniqueid_to_ino_t(fattr.cf_uniqueid);
tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr); tmp_dentry = cifs_readdir_lookup(file->f_dentry, &qstring, &fattr);
...@@ -777,9 +789,17 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) ...@@ -777,9 +789,17 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
xid = GetXid(); xid = GetXid();
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb); cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
pTcon = cifs_sb->tcon;
if (pTcon == NULL) /*
return -EINVAL; * Ensure FindFirst doesn't fail before doing filldir() for '.' and
* '..'. Otherwise we won't be able to notify VFS in case of failure.
*/
if (file->private_data == NULL) {
rc = initiate_cifs_search(xid, file);
cFYI(1, "initiate cifs search rc %d", rc);
if (rc)
goto rddir2_exit;
}
switch ((int) file->f_pos) { switch ((int) file->f_pos) {
case 0: case 0:
...@@ -804,14 +824,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) ...@@ -804,14 +824,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
if it before then restart search if it before then restart search
if after then keep searching till find it */ if after then keep searching till find it */
if (file->private_data == NULL) {
rc = initiate_cifs_search(xid, file);
cFYI(1, "initiate cifs search rc %d", rc);
if (rc) {
FreeXid(xid);
return rc;
}
}
if (file->private_data == NULL) { if (file->private_data == NULL) {
rc = -EINVAL; rc = -EINVAL;
FreeXid(xid); FreeXid(xid);
...@@ -829,6 +841,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir) ...@@ -829,6 +841,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
CIFSFindClose(xid, pTcon, cifsFile->netfid); CIFSFindClose(xid, pTcon, cifsFile->netfid);
} */ } */
pTcon = tlink_tcon(cifsFile->tlink);
rc = find_cifs_entry(xid, pTcon, file, rc = find_cifs_entry(xid, pTcon, file,
&current_entry, &num_to_fill); &current_entry, &num_to_fill);
if (rc) { if (rc) {
......
...@@ -80,7 +80,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses) ...@@ -80,7 +80,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses)
if (max_vcs < 2) if (max_vcs < 2)
max_vcs = 0xFFFF; max_vcs = 0xFFFF;
write_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if ((ses->need_reconnect) && is_first_ses_reconnect(ses)) if ((ses->need_reconnect) && is_first_ses_reconnect(ses))
goto get_vc_num_exit; /* vcnum will be zero */ goto get_vc_num_exit; /* vcnum will be zero */
for (i = ses->server->srv_count - 1; i < max_vcs; i++) { for (i = ses->server->srv_count - 1; i < max_vcs; i++) {
...@@ -112,7 +112,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses) ...@@ -112,7 +112,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses)
vcnum = i; vcnum = i;
ses->vcnum = vcnum; ses->vcnum = vcnum;
get_vc_num_exit: get_vc_num_exit:
write_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return cpu_to_le16(vcnum); return cpu_to_le16(vcnum);
} }
...@@ -383,6 +383,9 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft, ...@@ -383,6 +383,9 @@ static int decode_ascii_ssetup(char **pbcc_area, int bleft,
static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
struct cifsSesInfo *ses) struct cifsSesInfo *ses)
{ {
unsigned int tioffset; /* challenge message target info area */
unsigned int tilen; /* challenge message target info area length */
CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr; CHALLENGE_MESSAGE *pblob = (CHALLENGE_MESSAGE *)bcc_ptr;
if (blob_len < sizeof(CHALLENGE_MESSAGE)) { if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
...@@ -399,12 +402,25 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, ...@@ -399,12 +402,25 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
return -EINVAL; return -EINVAL;
} }
memcpy(ses->server->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE); memcpy(ses->cryptKey, pblob->Challenge, CIFS_CRYPTO_KEY_SIZE);
/* BB we could decode pblob->NegotiateFlags; some may be useful */ /* BB we could decode pblob->NegotiateFlags; some may be useful */
/* In particular we can examine sign flags */ /* In particular we can examine sign flags */
/* BB spec says that if AvId field of MsvAvTimestamp is populated then /* BB spec says that if AvId field of MsvAvTimestamp is populated then
we must set the MIC field of the AUTHENTICATE_MESSAGE */ we must set the MIC field of the AUTHENTICATE_MESSAGE */
tioffset = cpu_to_le16(pblob->TargetInfoArray.BufferOffset);
tilen = cpu_to_le16(pblob->TargetInfoArray.Length);
ses->tilen = tilen;
if (ses->tilen) {
ses->tiblob = kmalloc(tilen, GFP_KERNEL);
if (!ses->tiblob) {
cERROR(1, "Challenge target info allocation failure");
ses->tilen = 0;
return -ENOMEM;
}
memcpy(ses->tiblob, bcc_ptr + tioffset, ses->tilen);
}
return 0; return 0;
} }
...@@ -425,7 +441,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -425,7 +441,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
/* BB is NTLMV2 session security format easier to use here? */ /* BB is NTLMV2 session security format easier to use here? */
flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET | flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; NTLMSSP_NEGOTIATE_NTLM;
if (ses->server->secMode & if (ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= NTLMSSP_NEGOTIATE_SIGN;
...@@ -448,13 +464,16 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer, ...@@ -448,13 +464,16 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
maximum possible size is fixed and small, making this approach cleaner. maximum possible size is fixed and small, making this approach cleaner.
This function returns the length of the data in the blob */ This function returns the length of the data in the blob */
static int build_ntlmssp_auth_blob(unsigned char *pbuffer, static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
u16 *buflen,
struct cifsSesInfo *ses, struct cifsSesInfo *ses,
const struct nls_table *nls_cp, bool first) const struct nls_table *nls_cp)
{ {
int rc;
unsigned int size;
AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer; AUTHENTICATE_MESSAGE *sec_blob = (AUTHENTICATE_MESSAGE *)pbuffer;
__u32 flags; __u32 flags;
unsigned char *tmp; unsigned char *tmp;
char ntlm_session_key[CIFS_SESS_KEY_SIZE]; struct ntlmv2_resp ntlmv2_response = {};
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8); memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
sec_blob->MessageType = NtLmAuthenticate; sec_blob->MessageType = NtLmAuthenticate;
...@@ -462,7 +481,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -462,7 +481,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
flags = NTLMSSP_NEGOTIATE_56 | flags = NTLMSSP_NEGOTIATE_56 |
NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO | NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM; NTLMSSP_NEGOTIATE_NTLM;
if (ses->server->secMode & if (ses->server->secMode &
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
flags |= NTLMSSP_NEGOTIATE_SIGN; flags |= NTLMSSP_NEGOTIATE_SIGN;
...@@ -477,19 +496,26 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -477,19 +496,26 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
sec_blob->LmChallengeResponse.Length = 0; sec_blob->LmChallengeResponse.Length = 0;
sec_blob->LmChallengeResponse.MaximumLength = 0; sec_blob->LmChallengeResponse.MaximumLength = 0;
/* calculate session key, BB what about adding similar ntlmv2 path? */
SMBNTencrypt(ses->password, ses->server->cryptKey, ntlm_session_key);
if (first)
cifs_calculate_mac_key(&ses->server->mac_signing_key,
ntlm_session_key, ses->password);
memcpy(tmp, ntlm_session_key, CIFS_SESS_KEY_SIZE);
sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->NtChallengeResponse.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->NtChallengeResponse.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE); rc = setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp);
sec_blob->NtChallengeResponse.MaximumLength = if (rc) {
cpu_to_le16(CIFS_SESS_KEY_SIZE); cERROR(1, "Error %d during NTLMSSP authentication", rc);
goto setup_ntlmv2_ret;
}
size = sizeof(struct ntlmv2_resp);
memcpy(tmp, (char *)&ntlmv2_response, size);
tmp += size;
if (ses->tilen > 0) {
memcpy(tmp, ses->tiblob, ses->tilen);
tmp += ses->tilen;
}
tmp += CIFS_SESS_KEY_SIZE; sec_blob->NtChallengeResponse.Length = cpu_to_le16(size + ses->tilen);
sec_blob->NtChallengeResponse.MaximumLength =
cpu_to_le16(size + ses->tilen);
kfree(ses->tiblob);
ses->tiblob = NULL;
ses->tilen = 0;
if (ses->domainName == NULL) { if (ses->domainName == NULL) {
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
...@@ -501,7 +527,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -501,7 +527,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUCS((__le16 *)tmp, ses->domainName, len = cifs_strtoUCS((__le16 *)tmp, ses->domainName,
MAX_USERNAME_SIZE, nls_cp); MAX_USERNAME_SIZE, nls_cp);
len *= 2; /* unicode is 2 bytes each */ len *= 2; /* unicode is 2 bytes each */
len += 2; /* trailing null */
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->DomainName.Length = cpu_to_le16(len); sec_blob->DomainName.Length = cpu_to_le16(len);
sec_blob->DomainName.MaximumLength = cpu_to_le16(len); sec_blob->DomainName.MaximumLength = cpu_to_le16(len);
...@@ -518,7 +543,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -518,7 +543,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
len = cifs_strtoUCS((__le16 *)tmp, ses->userName, len = cifs_strtoUCS((__le16 *)tmp, ses->userName,
MAX_USERNAME_SIZE, nls_cp); MAX_USERNAME_SIZE, nls_cp);
len *= 2; /* unicode is 2 bytes each */ len *= 2; /* unicode is 2 bytes each */
len += 2; /* trailing null */
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->UserName.Length = cpu_to_le16(len); sec_blob->UserName.Length = cpu_to_le16(len);
sec_blob->UserName.MaximumLength = cpu_to_le16(len); sec_blob->UserName.MaximumLength = cpu_to_le16(len);
...@@ -533,7 +557,10 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer, ...@@ -533,7 +557,10 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer); sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
sec_blob->SessionKey.Length = 0; sec_blob->SessionKey.Length = 0;
sec_blob->SessionKey.MaximumLength = 0; sec_blob->SessionKey.MaximumLength = 0;
return tmp - pbuffer;
setup_ntlmv2_ret:
*buflen = tmp - pbuffer;
return rc;
} }
...@@ -545,19 +572,6 @@ static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB, ...@@ -545,19 +572,6 @@ static void setup_ntlmssp_neg_req(SESSION_SETUP_ANDX *pSMB,
return; return;
} }
static int setup_ntlmssp_auth_req(SESSION_SETUP_ANDX *pSMB,
struct cifsSesInfo *ses,
const struct nls_table *nls, bool first_time)
{
int bloblen;
bloblen = build_ntlmssp_auth_blob(&pSMB->req.SecurityBlob[0], ses, nls,
first_time);
pSMB->req.SecurityBlobLength = cpu_to_le16(bloblen);
return bloblen;
}
#endif #endif
int int
...@@ -579,15 +593,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -579,15 +593,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
int bytes_remaining; int bytes_remaining;
struct key *spnego_key = NULL; struct key *spnego_key = NULL;
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */ __le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
bool first_time; u16 blob_len;
char *ntlmsspblob = NULL;
if (ses == NULL) if (ses == NULL)
return -EINVAL; return -EINVAL;
read_lock(&cifs_tcp_ses_lock);
first_time = is_first_ses_reconnect(ses);
read_unlock(&cifs_tcp_ses_lock);
type = ses->server->secType; type = ses->server->secType;
cFYI(1, "sess setup type %d", type); cFYI(1, "sess setup type %d", type);
...@@ -658,7 +669,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -658,7 +669,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
/* BB calculate hash with password */ /* BB calculate hash with password */
/* and copy into bcc */ /* and copy into bcc */
calc_lanman_hash(ses->password, ses->server->cryptKey, calc_lanman_hash(ses->password, ses->cryptKey,
ses->server->secMode & SECMODE_PW_ENCRYPT ? ses->server->secMode & SECMODE_PW_ENCRYPT ?
true : false, lnm_session_key); true : false, lnm_session_key);
...@@ -685,15 +696,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -685,15 +696,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
cpu_to_le16(CIFS_SESS_KEY_SIZE); cpu_to_le16(CIFS_SESS_KEY_SIZE);
/* calculate session key */ /* calculate session key */
SMBNTencrypt(ses->password, ses->server->cryptKey, SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key);
ntlm_session_key);
if (first_time) /* should this be moved into common code cifs_calculate_session_key(&ses->auth_key,
with similar ntlmv2 path? */
cifs_calculate_mac_key(&ses->server->mac_signing_key,
ntlm_session_key, ses->password); ntlm_session_key, ses->password);
/* copy session key */ /* copy session key */
memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
bcc_ptr += CIFS_SESS_KEY_SIZE; bcc_ptr += CIFS_SESS_KEY_SIZE;
memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE); memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
...@@ -725,16 +732,31 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -725,16 +732,31 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0; pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
/* cpu_to_le16(LM2_SESS_KEY_SIZE); */ /* cpu_to_le16(LM2_SESS_KEY_SIZE); */
pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(sizeof(struct ntlmv2_resp));
/* calculate session key */ /* calculate session key */
setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp); rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
/* FIXME: calculate MAC key */ if (rc) {
cERROR(1, "Error %d during NTLMv2 authentication", rc);
kfree(v2_sess_key);
goto ssetup_exit;
}
memcpy(bcc_ptr, (char *)v2_sess_key, memcpy(bcc_ptr, (char *)v2_sess_key,
sizeof(struct ntlmv2_resp)); sizeof(struct ntlmv2_resp));
bcc_ptr += sizeof(struct ntlmv2_resp); bcc_ptr += sizeof(struct ntlmv2_resp);
kfree(v2_sess_key); kfree(v2_sess_key);
/* set case sensitive password length after tilen may get
* assigned, tilen is 0 otherwise.
*/
pSMB->req_no_secext.CaseSensitivePasswordLength =
cpu_to_le16(sizeof(struct ntlmv2_resp) + ses->tilen);
if (ses->tilen > 0) {
memcpy(bcc_ptr, ses->tiblob, ses->tilen);
bcc_ptr += ses->tilen;
/* we never did allocate ses->domainName to free */
kfree(ses->tiblob);
ses->tiblob = NULL;
ses->tilen = 0;
}
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
if (iov[0].iov_len % 2) { if (iov[0].iov_len % 2) {
*bcc_ptr = 0; *bcc_ptr = 0;
...@@ -765,17 +787,14 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -765,17 +787,14 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
} }
/* bail out if key is too long */ /* bail out if key is too long */
if (msg->sesskey_len > if (msg->sesskey_len >
sizeof(ses->server->mac_signing_key.data.krb5)) { sizeof(ses->auth_key.data.krb5)) {
cERROR(1, "Kerberos signing key too long (%u bytes)", cERROR(1, "Kerberos signing key too long (%u bytes)",
msg->sesskey_len); msg->sesskey_len);
rc = -EOVERFLOW; rc = -EOVERFLOW;
goto ssetup_exit; goto ssetup_exit;
} }
if (first_time) { ses->auth_key.len = msg->sesskey_len;
ses->server->mac_signing_key.len = msg->sesskey_len; memcpy(ses->auth_key.data.krb5, msg->data, msg->sesskey_len);
memcpy(ses->server->mac_signing_key.data.krb5,
msg->data, msg->sesskey_len);
}
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC; pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
capabilities |= CAP_EXTENDED_SECURITY; capabilities |= CAP_EXTENDED_SECURITY;
pSMB->req.Capabilities = cpu_to_le32(capabilities); pSMB->req.Capabilities = cpu_to_le32(capabilities);
...@@ -815,12 +834,30 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -815,12 +834,30 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
if (phase == NtLmNegotiate) { if (phase == NtLmNegotiate) {
setup_ntlmssp_neg_req(pSMB, ses); setup_ntlmssp_neg_req(pSMB, ses);
iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE); iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
iov[1].iov_base = &pSMB->req.SecurityBlob[0];
} else if (phase == NtLmAuthenticate) { } else if (phase == NtLmAuthenticate) {
int blob_len; /* 5 is an empirical value, large enought to
blob_len = setup_ntlmssp_auth_req(pSMB, ses, * hold authenticate message, max 10 of
nls_cp, * av paris, doamin,user,workstation mames,
first_time); * flags etc..
*/
ntlmsspblob = kmalloc(
5*sizeof(struct _AUTHENTICATE_MESSAGE),
GFP_KERNEL);
if (!ntlmsspblob) {
cERROR(1, "Can't allocate NTLMSSP");
rc = -ENOMEM;
goto ssetup_exit;
}
rc = build_ntlmssp_auth_blob(ntlmsspblob,
&blob_len, ses, nls_cp);
if (rc)
goto ssetup_exit;
iov[1].iov_len = blob_len; iov[1].iov_len = blob_len;
iov[1].iov_base = ntlmsspblob;
pSMB->req.SecurityBlobLength =
cpu_to_le16(blob_len);
/* Make sure that we tell the server that we /* Make sure that we tell the server that we
are using the uid that it just gave us back are using the uid that it just gave us back
on the response (challenge) */ on the response (challenge) */
...@@ -830,7 +867,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -830,7 +867,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
rc = -ENOSYS; rc = -ENOSYS;
goto ssetup_exit; goto ssetup_exit;
} }
iov[1].iov_base = &pSMB->req.SecurityBlob[0];
/* unicode strings must be word aligned */ /* unicode strings must be word aligned */
if ((iov[0].iov_len + iov[1].iov_len) % 2) { if ((iov[0].iov_len + iov[1].iov_len) % 2) {
*bcc_ptr = 0; *bcc_ptr = 0;
...@@ -895,7 +931,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -895,7 +931,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
bcc_ptr = pByteArea(smb_buf); bcc_ptr = pByteArea(smb_buf);
if (smb_buf->WordCount == 4) { if (smb_buf->WordCount == 4) {
__u16 blob_len;
blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength); blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
if (blob_len > bytes_remaining) { if (blob_len > bytes_remaining) {
cERROR(1, "bad security blob length %d", blob_len); cERROR(1, "bad security blob length %d", blob_len);
...@@ -931,6 +966,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses, ...@@ -931,6 +966,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
key_put(spnego_key); key_put(spnego_key);
} }
kfree(str_area); kfree(str_area);
kfree(ntlmsspblob);
ntlmsspblob = NULL;
if (resp_buf_type == CIFS_SMALL_BUFFER) { if (resp_buf_type == CIFS_SMALL_BUFFER) {
cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base); cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base);
cifs_small_buf_release(iov[0].iov_base); cifs_small_buf_release(iov[0].iov_base);
......
...@@ -543,7 +543,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -543,7 +543,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED))) { SECMODE_SIGN_ENABLED))) {
rc = cifs_verify_signature(midQ->resp_buf, rc = cifs_verify_signature(midQ->resp_buf,
&ses->server->mac_signing_key, &ses->server->session_key,
midQ->sequence_number+1); midQ->sequence_number+1);
if (rc) { if (rc) {
cERROR(1, "Unexpected SMB signature"); cERROR(1, "Unexpected SMB signature");
...@@ -731,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses, ...@@ -731,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED))) { SECMODE_SIGN_ENABLED))) {
rc = cifs_verify_signature(out_buf, rc = cifs_verify_signature(out_buf,
&ses->server->mac_signing_key, &ses->server->session_key,
midQ->sequence_number+1); midQ->sequence_number+1);
if (rc) { if (rc) {
cERROR(1, "Unexpected SMB signature"); cERROR(1, "Unexpected SMB signature");
...@@ -981,7 +981,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, ...@@ -981,7 +981,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
SECMODE_SIGN_ENABLED))) { SECMODE_SIGN_ENABLED))) {
rc = cifs_verify_signature(out_buf, rc = cifs_verify_signature(out_buf,
&ses->server->mac_signing_key, &ses->server->session_key,
midQ->sequence_number+1); midQ->sequence_number+1);
if (rc) { if (rc) {
cERROR(1, "Unexpected SMB signature"); cERROR(1, "Unexpected SMB signature");
......
...@@ -47,9 +47,10 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) ...@@ -47,9 +47,10 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct super_block *sb; struct super_block *sb;
char *full_path; char *full_path = NULL;
if (direntry == NULL) if (direntry == NULL)
return -EIO; return -EIO;
...@@ -58,16 +59,19 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) ...@@ -58,16 +59,19 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
sb = direntry->d_inode->i_sb; sb = direntry->d_inode->i_sb;
if (sb == NULL) if (sb == NULL)
return -EIO; return -EIO;
xid = GetXid();
cifs_sb = CIFS_SB(sb); cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto remove_ea_exit;
return rc;
} }
if (ea_name == NULL) { if (ea_name == NULL) {
cFYI(1, "Null xattr names not supported"); cFYI(1, "Null xattr names not supported");
...@@ -91,6 +95,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name) ...@@ -91,6 +95,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
remove_ea_exit: remove_ea_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
#endif #endif
return rc; return rc;
} }
...@@ -102,6 +107,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, ...@@ -102,6 +107,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct super_block *sb; struct super_block *sb;
char *full_path; char *full_path;
...@@ -113,16 +119,19 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, ...@@ -113,16 +119,19 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
sb = direntry->d_inode->i_sb; sb = direntry->d_inode->i_sb;
if (sb == NULL) if (sb == NULL)
return -EIO; return -EIO;
xid = GetXid();
cifs_sb = CIFS_SB(sb); cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto set_ea_exit;
return rc;
} }
/* return dos attributes as pseudo xattr */ /* return dos attributes as pseudo xattr */
/* return alt name if available as pseudo attr */ /* return alt name if available as pseudo attr */
...@@ -132,9 +141,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, ...@@ -132,9 +141,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
returns as xattrs */ returns as xattrs */
if (value_size > MAX_EA_VALUE_SIZE) { if (value_size > MAX_EA_VALUE_SIZE) {
cFYI(1, "size of EA value too large"); cFYI(1, "size of EA value too large");
kfree(full_path); rc = -EOPNOTSUPP;
FreeXid(xid); goto set_ea_exit;
return -EOPNOTSUPP;
} }
if (ea_name == NULL) { if (ea_name == NULL) {
...@@ -198,6 +206,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name, ...@@ -198,6 +206,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
set_ea_exit: set_ea_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
#endif #endif
return rc; return rc;
} }
...@@ -209,6 +218,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, ...@@ -209,6 +218,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct super_block *sb; struct super_block *sb;
char *full_path; char *full_path;
...@@ -221,16 +231,18 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, ...@@ -221,16 +231,18 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
if (sb == NULL) if (sb == NULL)
return -EIO; return -EIO;
xid = GetXid();
cifs_sb = CIFS_SB(sb); cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon; tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto get_ea_exit;
return rc;
} }
/* return dos attributes as pseudo xattr */ /* return dos attributes as pseudo xattr */
/* return alt name if available as pseudo attr */ /* return alt name if available as pseudo attr */
...@@ -323,6 +335,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name, ...@@ -323,6 +335,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
get_ea_exit: get_ea_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
#endif #endif
return rc; return rc;
} }
...@@ -333,6 +346,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) ...@@ -333,6 +346,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
#ifdef CONFIG_CIFS_XATTR #ifdef CONFIG_CIFS_XATTR
int xid; int xid;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct tcon_link *tlink;
struct cifsTconInfo *pTcon; struct cifsTconInfo *pTcon;
struct super_block *sb; struct super_block *sb;
char *full_path; char *full_path;
...@@ -346,18 +360,20 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) ...@@ -346,18 +360,20 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
return -EIO; return -EIO;
cifs_sb = CIFS_SB(sb); cifs_sb = CIFS_SB(sb);
pTcon = cifs_sb->tcon;
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
return -EOPNOTSUPP; return -EOPNOTSUPP;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
xid = GetXid(); xid = GetXid();
full_path = build_path_from_dentry(direntry); full_path = build_path_from_dentry(direntry);
if (full_path == NULL) { if (full_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
FreeXid(xid); goto list_ea_exit;
return rc;
} }
/* return dos attributes as pseudo xattr */ /* return dos attributes as pseudo xattr */
/* return alt name if available as pseudo attr */ /* return alt name if available as pseudo attr */
...@@ -370,8 +386,10 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size) ...@@ -370,8 +386,10 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
cifs_sb->mnt_cifs_flags & cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
list_ea_exit:
kfree(full_path); kfree(full_path);
FreeXid(xid); FreeXid(xid);
cifs_put_tlink(tlink);
#endif #endif
return rc; return rc;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment