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
This commit is contained in:
commit
d2ecad9fac
27 changed files with 2097 additions and 1049 deletions
|
@ -527,6 +527,11 @@ A partial list of the supported mount options follows:
|
|||
SFU does). In the future the bottom 9 bits of the
|
||||
mode also will be emulated using queries of the security
|
||||
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
|
||||
by intermediate systems in the route). Note that signing
|
||||
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)
|
|||
seq_printf(m, "Servers:");
|
||||
|
||||
i = 0;
|
||||
read_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp1, &cifs_tcp_ses_list) {
|
||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||
tcp_ses_list);
|
||||
|
@ -230,7 +230,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
|
|||
spin_unlock(&GlobalMid_Lock);
|
||||
}
|
||||
}
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
seq_putc(m, '\n');
|
||||
|
||||
/* 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,
|
|||
atomic_set(&totBufAllocCount, 0);
|
||||
atomic_set(&totSmBufAllocCount, 0);
|
||||
#endif /* CONFIG_CIFS_STATS2 */
|
||||
read_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp1, &cifs_tcp_ses_list) {
|
||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||
tcp_ses_list);
|
||||
|
@ -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;
|
||||
|
@ -343,7 +343,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
|
|||
GlobalCurrentXid, GlobalMaxActiveXid);
|
||||
|
||||
i = 0;
|
||||
read_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp1, &cifs_tcp_ses_list) {
|
||||
server = list_entry(tmp1, struct TCP_Server_Info,
|
||||
tcp_ses_list);
|
||||
|
@ -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');
|
||||
return 0;
|
||||
|
|
|
@ -34,7 +34,7 @@ void cifs_dump_mids(struct TCP_Server_Info *);
|
|||
extern int traceSMB; /* flag which enables the function below */
|
||||
void dump_smb(struct smb_hdr *, int);
|
||||
#define CIFS_INFO 0x01
|
||||
#define CIFS_RC 0x02
|
||||
#define CIFS_RC 0x02
|
||||
#define CIFS_TIMER 0x04
|
||||
|
||||
/*
|
||||
|
|
|
@ -44,8 +44,7 @@ static void cifs_dfs_expire_automounts(struct work_struct *work)
|
|||
void cifs_dfs_release_automount_timer(void)
|
||||
{
|
||||
BUG_ON(!list_empty(&cifs_dfs_automount_list));
|
||||
cancel_delayed_work(&cifs_dfs_automount_task);
|
||||
flush_scheduled_work();
|
||||
cancel_delayed_work_sync(&cifs_dfs_automount_task);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -306,6 +305,7 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
|||
int xid, i;
|
||||
int rc = 0;
|
||||
struct vfsmount *mnt = ERR_PTR(-ENOENT);
|
||||
struct tcon_link *tlink;
|
||||
|
||||
cFYI(1, "in %s", __func__);
|
||||
BUG_ON(IS_ROOT(dentry));
|
||||
|
@ -315,14 +315,6 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
|||
dput(nd->path.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
|
||||
* responses must be prefixed by a single '\' character instead of
|
||||
|
@ -335,10 +327,20 @@ cifs_dfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd)
|
|||
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,
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
for (i = 0; i < num_referrals; i++) {
|
||||
int len;
|
||||
dump_referral(referrals+i);
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
* the GNU Lesser General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#include <linux/radix-tree.h>
|
||||
|
||||
#ifndef _CIFS_FS_SB_H
|
||||
#define _CIFS_FS_SB_H
|
||||
|
||||
|
@ -36,23 +38,28 @@
|
|||
#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_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 cifsTconInfo *tcon; /* primary mount */
|
||||
struct list_head nested_tcon_q;
|
||||
struct radix_tree_root tlink_tree;
|
||||
#define CIFS_TLINK_MASTER_TAG 0 /* is "master" (mount) tcon */
|
||||
spinlock_t tlink_tree_lock;
|
||||
struct nls_table *local_nls;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
atomic_t active;
|
||||
uid_t mnt_uid;
|
||||
gid_t mnt_gid;
|
||||
mode_t mnt_file_mode;
|
||||
mode_t mnt_dir_mode;
|
||||
int mnt_cifs_flags;
|
||||
unsigned int mnt_cifs_flags;
|
||||
int prepathlen;
|
||||
char *prepath; /* relative path under the share to mount to */
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
char *mountdata; /* mount options received at mount time */
|
||||
#endif
|
||||
struct backing_dev_info bdi;
|
||||
struct delayed_work prune_tlinks;
|
||||
};
|
||||
#endif /* _CIFS_FS_SB_H */
|
||||
|
|
|
@ -557,11 +557,16 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,
|
|||
{
|
||||
struct cifs_ntsd *pntsd = NULL;
|
||||
int xid, rc;
|
||||
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
|
||||
|
||||
if (IS_ERR(tlink))
|
||||
return NULL;
|
||||
|
||||
xid = GetXid();
|
||||
rc = CIFSSMBGetCIFSACL(xid, cifs_sb->tcon, fid, &pntsd, pacllen);
|
||||
rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen);
|
||||
FreeXid(xid);
|
||||
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
cFYI(1, "GetCIFSACL rc = %d ACL len %d", rc, *pacllen);
|
||||
return pntsd;
|
||||
|
@ -574,10 +579,16 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
|
|||
int oplock = 0;
|
||||
int xid, rc;
|
||||
__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();
|
||||
|
||||
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,
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
if (rc) {
|
||||
|
@ -585,11 +596,12 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,
|
|||
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);
|
||||
|
||||
CIFSSMBClose(xid, cifs_sb->tcon, fid);
|
||||
CIFSSMBClose(xid, tcon, fid);
|
||||
out:
|
||||
cifs_put_tlink(tlink);
|
||||
FreeXid(xid);
|
||||
return pntsd;
|
||||
}
|
||||
|
@ -603,7 +615,7 @@ static struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,
|
|||
struct cifsFileInfo *open_file = NULL;
|
||||
|
||||
if (inode)
|
||||
open_file = find_readable_file(CIFS_I(inode));
|
||||
open_file = find_readable_file(CIFS_I(inode), true);
|
||||
if (!open_file)
|
||||
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,
|
|||
struct cifs_ntsd *pnntsd, u32 acllen)
|
||||
{
|
||||
int xid, rc;
|
||||
struct tcon_link *tlink = cifs_sb_tlink(cifs_sb);
|
||||
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
|
||||
xid = GetXid();
|
||||
rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
|
||||
rc = CIFSSMBSetCIFSACL(xid, tlink_tcon(tlink), fid, pnntsd, acllen);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
cFYI(DBG2, "SetCIFSACL rc = %d", rc);
|
||||
return rc;
|
||||
|
@ -631,10 +648,16 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
|
|||
int oplock = 0;
|
||||
int xid, rc;
|
||||
__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();
|
||||
|
||||
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,
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
if (rc) {
|
||||
|
@ -642,12 +665,13 @@ static int set_cifs_acl_by_path(struct cifs_sb_info *cifs_sb, const char *path,
|
|||
goto out;
|
||||
}
|
||||
|
||||
rc = CIFSSMBSetCIFSACL(xid, cifs_sb->tcon, fid, pnntsd, acllen);
|
||||
rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen);
|
||||
cFYI(DBG2, "SetCIFSACL rc = %d", rc);
|
||||
|
||||
CIFSSMBClose(xid, cifs_sb->tcon, fid);
|
||||
out:
|
||||
CIFSSMBClose(xid, tcon, fid);
|
||||
out:
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
||||
open_file = find_readable_file(CIFS_I(inode));
|
||||
open_file = find_readable_file(CIFS_I(inode), true);
|
||||
if (!open_file)
|
||||
return set_cifs_acl_by_path(cifs_sb, path, pnntsd, acllen);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "md5.h"
|
||||
#include "cifs_unicode.h"
|
||||
#include "cifsproto.h"
|
||||
#include "ntlmssp.h"
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/random.h>
|
||||
|
||||
|
@ -42,7 +43,7 @@ extern void SMBencrypt(unsigned char *passwd, const unsigned char *c8,
|
|||
unsigned char *p24);
|
||||
|
||||
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;
|
||||
|
||||
|
@ -78,7 +79,7 @@ int cifs_sign_smb(struct smb_hdr *cifs_pdu, struct TCP_Server_Info *server,
|
|||
server->sequence_number++;
|
||||
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);
|
||||
if (rc)
|
||||
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,
|
|||
}
|
||||
|
||||
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;
|
||||
int i;
|
||||
|
@ -145,7 +146,7 @@ int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *server,
|
|||
server->sequence_number++;
|
||||
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);
|
||||
if (rc)
|
||||
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,
|
|||
}
|
||||
|
||||
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)
|
||||
{
|
||||
unsigned int rc;
|
||||
char server_response_sig[8];
|
||||
char what_we_think_sig_should_be[20];
|
||||
|
||||
if ((cifs_pdu == NULL) || (mac_key == NULL))
|
||||
if (cifs_pdu == NULL || session_key == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
if (cifs_pdu->Command == SMB_COM_NEGOTIATE)
|
||||
|
@ -192,7 +193,7 @@ int cifs_verify_signature(struct smb_hdr *cifs_pdu,
|
|||
cpu_to_le32(expected_sequence_number);
|
||||
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);
|
||||
|
||||
if (rc)
|
||||
|
@ -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 */
|
||||
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)
|
||||
{
|
||||
char temp_key[16];
|
||||
|
@ -262,6 +263,148 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
|
|||
}
|
||||
#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,
|
||||
const struct nls_table *nls_cp)
|
||||
{
|
||||
|
@ -315,13 +458,14 @@ static int calc_ntlmv2_hash(struct cifsSesInfo *ses,
|
|||
calc_exit_2:
|
||||
/* BB FIXME what about bytes 24 through 40 of the signing key?
|
||||
compare with the NTLM example */
|
||||
hmac_md5_final(ses->server->ntlmv2_hash, pctxt);
|
||||
hmac_md5_final(ses->ntlmv2_hash, pctxt);
|
||||
|
||||
kfree(pctxt);
|
||||
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)
|
||||
{
|
||||
int rc;
|
||||
|
@ -333,25 +477,48 @@ void setup_ntlmv2_rsp(struct cifsSesInfo *ses, char *resp_buf,
|
|||
buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
|
||||
get_random_bytes(&buf->client_chal, sizeof(buf->client_chal));
|
||||
buf->reserved2 = 0;
|
||||
buf->names[0].type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
|
||||
buf->names[0].length = 0;
|
||||
buf->names[1].type = 0;
|
||||
buf->names[1].length = 0;
|
||||
|
||||
if (ses->server->secType == RawNTLMSSP) {
|
||||
if (!ses->domainName) {
|
||||
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 */
|
||||
rc = calc_ntlmv2_hash(ses, nls_cp);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
cERROR(1, "could not get v2 hash rc %d", rc);
|
||||
goto setup_ntlmv2_rsp_ret;
|
||||
}
|
||||
CalcNTLMv2_response(ses, resp_buf);
|
||||
|
||||
/* now calculate the MAC key for NTLMv2 */
|
||||
hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
|
||||
/* now calculate the session key for NTLMv2 */
|
||||
hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 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));
|
||||
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,
|
||||
|
@ -359,12 +526,15 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses,
|
|||
{
|
||||
struct HMACMD5Context context;
|
||||
/* rest of v2 struct already generated */
|
||||
memcpy(v2_session_response + 8, ses->server->cryptKey, 8);
|
||||
hmac_md5_init_limK_to_64(ses->server->ntlmv2_hash, 16, &context);
|
||||
memcpy(v2_session_response + 8, ses->cryptKey, 8);
|
||||
hmac_md5_init_limK_to_64(ses->ntlmv2_hash, 16, &context);
|
||||
|
||||
hmac_md5_update(v2_session_response+8,
|
||||
sizeof(struct ntlmv2_resp) - 8, &context);
|
||||
|
||||
if (ses->tilen)
|
||||
hmac_md5_update(ses->tiblob, ses->tilen, &context);
|
||||
|
||||
hmac_md5_final(v2_session_response, &context);
|
||||
/* cifs_dump_mem("v2_sess_rsp: ", v2_session_response, 32); */
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <net/ipv6.h>
|
||||
#include "cifsfs.h"
|
||||
#include "cifspdu.h"
|
||||
#define DECLARE_GLOBALS_HERE
|
||||
|
@ -81,6 +82,24 @@ extern mempool_t *cifs_sm_req_poolp;
|
|||
extern mempool_t *cifs_req_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
|
||||
cifs_read_super(struct super_block *sb, void *data,
|
||||
const char *devname, int silent)
|
||||
|
@ -96,6 +115,9 @@ cifs_read_super(struct super_block *sb, void *data,
|
|||
if (cifs_sb == NULL)
|
||||
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);
|
||||
if (rc) {
|
||||
kfree(cifs_sb);
|
||||
|
@ -135,9 +157,6 @@ cifs_read_super(struct super_block *sb, void *data,
|
|||
sb->s_magic = CIFS_MAGIC_NUMBER;
|
||||
sb->s_op = &cifs_super_ops;
|
||||
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_bits = 14; /* default 2**14 = CIFS_MAX_MSGSIZE */
|
||||
inode = cifs_root_iget(sb, ROOT_I);
|
||||
|
@ -219,7 +238,7 @@ cifs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|||
{
|
||||
struct super_block *sb = dentry->d_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 xid;
|
||||
|
||||
|
@ -361,14 +380,36 @@ static int
|
|||
cifs_show_options(struct seq_file *s, struct vfsmount *m)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
if (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);
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID)
|
||||
seq_printf(s, ",forceuid");
|
||||
|
@ -417,6 +458,8 @@ cifs_show_options(struct seq_file *s, struct vfsmount *m)
|
|||
seq_printf(s, ",dynperm");
|
||||
if (m->mnt_sb->s_flags & MS_POSIXACL)
|
||||
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, ",wsize=%d", cifs_sb->wsize);
|
||||
|
@ -432,20 +475,18 @@ static void cifs_umount_begin(struct super_block *sb)
|
|||
if (cifs_sb == NULL)
|
||||
return;
|
||||
|
||||
tcon = cifs_sb->tcon;
|
||||
if (tcon == NULL)
|
||||
return;
|
||||
tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
read_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
|
||||
/* we have other mounts to same share or we have
|
||||
already tried to force umount this and woken up
|
||||
all waiting network requests, nothing to do */
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
} else if (tcon->tc_count == 1)
|
||||
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_notify_requests(tcon); */
|
||||
|
@ -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
|
||||
to protect *lease from going away */
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
|
||||
if (!(S_ISREG(inode->i_mode)))
|
||||
return -EINVAL;
|
||||
|
@ -575,8 +617,8 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
|
|||
((arg == F_WRLCK) &&
|
||||
(CIFS_I(inode)->clientCanCacheAll)))
|
||||
return generic_setlease(file, arg, lease);
|
||||
else if (CIFS_SB(inode->i_sb)->tcon->local_lease &&
|
||||
!CIFS_I(inode)->clientCanCacheRead)
|
||||
else if (tlink_tcon(cfile->tlink)->local_lease &&
|
||||
!CIFS_I(inode)->clientCanCacheRead)
|
||||
/* If the server claims to support oplock on this
|
||||
file, then we still need to check oplock even
|
||||
if the local_lease mount option is set, but there
|
||||
|
@ -895,8 +937,8 @@ init_cifs(void)
|
|||
GlobalTotalActiveXid = 0;
|
||||
GlobalMaxActiveXid = 0;
|
||||
memset(Local_System_Name, 0, 15);
|
||||
rwlock_init(&GlobalSMBSeslock);
|
||||
rwlock_init(&cifs_tcp_ses_lock);
|
||||
spin_lock_init(&cifs_tcp_ses_lock);
|
||||
spin_lock_init(&cifs_file_list_lock);
|
||||
spin_lock_init(&GlobalMid_Lock);
|
||||
|
||||
if (cifs_max_pending < 2) {
|
||||
|
@ -909,11 +951,11 @@ init_cifs(void)
|
|||
|
||||
rc = cifs_fscache_register();
|
||||
if (rc)
|
||||
goto out;
|
||||
goto out_clean_proc;
|
||||
|
||||
rc = cifs_init_inodecache();
|
||||
if (rc)
|
||||
goto out_clean_proc;
|
||||
goto out_unreg_fscache;
|
||||
|
||||
rc = cifs_init_mids();
|
||||
if (rc)
|
||||
|
@ -935,19 +977,19 @@ init_cifs(void)
|
|||
return 0;
|
||||
|
||||
#ifdef CONFIG_CIFS_UPCALL
|
||||
out_unregister_filesystem:
|
||||
out_unregister_filesystem:
|
||||
unregister_filesystem(&cifs_fs_type);
|
||||
#endif
|
||||
out_destroy_request_bufs:
|
||||
out_destroy_request_bufs:
|
||||
cifs_destroy_request_bufs();
|
||||
out_destroy_mids:
|
||||
out_destroy_mids:
|
||||
cifs_destroy_mids();
|
||||
out_destroy_inodecache:
|
||||
out_destroy_inodecache:
|
||||
cifs_destroy_inodecache();
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
out_unreg_fscache:
|
||||
cifs_fscache_unregister();
|
||||
out:
|
||||
out_clean_proc:
|
||||
cifs_proc_clean();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,8 @@ extern const struct address_space_operations cifs_addr_ops;
|
|||
extern const struct address_space_operations cifs_addr_ops_smallbuf;
|
||||
|
||||
/* Functions related to super block operations */
|
||||
/* extern const struct super_operations cifs_super_ops;*/
|
||||
extern void cifs_read_inode(struct inode *);
|
||||
/*extern void cifs_delete_inode(struct inode *);*/ /* BB not needed yet */
|
||||
/* extern void cifs_write_inode(struct inode *); */ /* BB not needed yet */
|
||||
extern void cifs_sb_active(struct super_block *sb);
|
||||
extern void cifs_sb_deactive(struct super_block *sb);
|
||||
|
||||
/* Functions related to inodes */
|
||||
extern const struct inode_operations cifs_dir_inode_ops;
|
||||
|
@ -104,7 +102,7 @@ extern int cifs_readlink(struct dentry *direntry, char __user *buffer,
|
|||
extern int cifs_symlink(struct inode *inode, struct dentry *direntry,
|
||||
const char *symname);
|
||||
extern int cifs_removexattr(struct dentry *, const char *);
|
||||
extern int cifs_setxattr(struct dentry *, const char *, const void *,
|
||||
extern int cifs_setxattr(struct dentry *, const char *, const void *,
|
||||
size_t, int);
|
||||
extern ssize_t cifs_getxattr(struct dentry *, const char *, void *, size_t);
|
||||
extern ssize_t cifs_listxattr(struct dentry *, char *, size_t);
|
||||
|
@ -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;
|
||||
#endif /* EXPERIMENTAL */
|
||||
|
||||
#define CIFS_VERSION "1.65"
|
||||
#define CIFS_VERSION "1.67"
|
||||
#endif /* _CIFSFS_H */
|
||||
|
|
|
@ -97,7 +97,7 @@ enum protocolEnum {
|
|||
/* Netbios frames protocol not supported at this time */
|
||||
};
|
||||
|
||||
struct mac_key {
|
||||
struct session_key {
|
||||
unsigned int len;
|
||||
union {
|
||||
char ntlm[CIFS_SESS_KEY_SIZE + 16];
|
||||
|
@ -139,6 +139,7 @@ struct TCP_Server_Info {
|
|||
struct sockaddr_in sockAddr;
|
||||
struct sockaddr_in6 sockAddr6;
|
||||
} addr;
|
||||
struct sockaddr_storage srcaddr; /* locally bind to this IP */
|
||||
wait_queue_head_t response_q;
|
||||
wait_queue_head_t request_q; /* if more than maxmpx to srvr must block*/
|
||||
struct list_head pending_mid_q;
|
||||
|
@ -178,12 +179,10 @@ struct TCP_Server_Info {
|
|||
int capabilities; /* allow selective disabling of caps by smb sess */
|
||||
int timeAdj; /* Adjust for difference in server time zone in sec */
|
||||
__u16 CurrentMid; /* multiplex id - rotating counter */
|
||||
char cryptKey[CIFS_CRYPTO_KEY_SIZE];
|
||||
/* 16th byte of RFC1001 workstation name is always null */
|
||||
char workstation_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
|
||||
__u32 sequence_number; /* needed for CIFS PDU signature */
|
||||
struct mac_key mac_signing_key;
|
||||
char ntlmv2_hash[16];
|
||||
struct session_key session_key;
|
||||
unsigned long lstrp; /* when we got last response from this server */
|
||||
u16 dialect; /* dialect index that server chose */
|
||||
/* extended security flavors that server supports */
|
||||
|
@ -191,6 +190,7 @@ struct TCP_Server_Info {
|
|||
bool sec_mskerberos; /* supports legacy MS Kerberos */
|
||||
bool sec_kerberosu2u; /* supports U2U Kerberos */
|
||||
bool sec_ntlmssp; /* supports NTLMSSP */
|
||||
bool session_estab; /* mark when very first sess is established */
|
||||
#ifdef CONFIG_CIFS_FSCACHE
|
||||
struct fscache_cookie *fscache; /* client index cache cookie */
|
||||
#endif
|
||||
|
@ -222,6 +222,11 @@ struct cifsSesInfo {
|
|||
char userName[MAX_USERNAME_SIZE + 1];
|
||||
char *domainName;
|
||||
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 */
|
||||
};
|
||||
/* no more than one of the following three session flags may be set */
|
||||
|
@ -307,6 +312,44 @@ struct cifsTconInfo {
|
|||
/* 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 is used to track byte stream locks on the file
|
||||
|
@ -345,12 +388,11 @@ struct cifsFileInfo {
|
|||
__u16 netfid; /* file id from remote */
|
||||
/* BB add lock scope info here if needed */ ;
|
||||
/* lock scope id (0 if none) */
|
||||
struct file *pfile; /* needed for writepage */
|
||||
struct inode *pInode; /* needed for oplock break */
|
||||
struct vfsmount *mnt;
|
||||
struct dentry *dentry;
|
||||
unsigned int f_flags;
|
||||
struct tcon_link *tlink;
|
||||
struct mutex lock_mutex;
|
||||
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 oplock_break_cancelled:1;
|
||||
atomic_t count; /* reference count */
|
||||
|
@ -365,14 +407,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
|||
atomic_inc(&cifs_file->count);
|
||||
}
|
||||
|
||||
/* Release a reference on the file private data */
|
||||
static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
if (atomic_dec_and_test(&cifs_file->count)) {
|
||||
iput(cifs_file->pInode);
|
||||
kfree(cifs_file);
|
||||
}
|
||||
}
|
||||
void cifsFileInfo_put(struct cifsFileInfo *cifs_file);
|
||||
|
||||
/*
|
||||
* One of these for each file inode
|
||||
|
@ -474,16 +509,16 @@ struct oplock_q_entry {
|
|||
|
||||
/* for pending dnotify requests */
|
||||
struct dir_notify_req {
|
||||
struct list_head lhead;
|
||||
__le16 Pid;
|
||||
__le16 PidHigh;
|
||||
__u16 Mid;
|
||||
__u16 Tid;
|
||||
__u16 Uid;
|
||||
__u16 netfid;
|
||||
__u32 filter; /* CompletionFilter (for multishot) */
|
||||
int multishot;
|
||||
struct file *pfile;
|
||||
struct list_head lhead;
|
||||
__le16 Pid;
|
||||
__le16 PidHigh;
|
||||
__u16 Mid;
|
||||
__u16 Tid;
|
||||
__u16 Uid;
|
||||
__u16 netfid;
|
||||
__u32 filter; /* CompletionFilter (for multishot) */
|
||||
int multishot;
|
||||
struct file *pfile;
|
||||
};
|
||||
|
||||
struct dfs_info3_param {
|
||||
|
@ -667,7 +702,7 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
|
|||
* the reference counters for the server, smb session, and tcon. Finally,
|
||||
* 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
|
||||
|
@ -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
|
||||
* 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 */
|
||||
GLOBAL_EXTERN struct list_head GlobalDnotifyReqList;
|
||||
|
|
|
@ -663,7 +663,6 @@ struct ntlmv2_resp {
|
|||
__le64 time;
|
||||
__u64 client_chal; /* random */
|
||||
__u32 reserved2;
|
||||
struct ntlmssp2_name names[2];
|
||||
/* array of name entries could follow ending in minimum 4 byte struct */
|
||||
} __attribute__((packed));
|
||||
|
||||
|
|
|
@ -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,
|
||||
struct TCP_Server_Info *);
|
||||
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
|
||||
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *);
|
||||
extern struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *, bool);
|
||||
#endif
|
||||
extern unsigned int smbCalcSize(struct smb_hdr *ptr);
|
||||
extern unsigned int smbCalcSize_LE(struct smb_hdr *ptr);
|
||||
|
@ -105,12 +105,12 @@ extern u64 cifs_UnixTimeToNT(struct timespec);
|
|||
extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
|
||||
int offset);
|
||||
|
||||
extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
|
||||
__u16 fileHandle, struct file *file,
|
||||
struct vfsmount *mnt, unsigned int oflags);
|
||||
extern struct cifsFileInfo *cifs_new_fileinfo(__u16 fileHandle,
|
||||
struct file *file, struct tcon_link *tlink,
|
||||
__u32 oplock);
|
||||
extern int cifs_posix_open(char *full_path, struct inode **pinode,
|
||||
struct super_block *sb,
|
||||
int mode, int oflags,
|
||||
int mode, unsigned int f_flags,
|
||||
__u32 *poplock, __u16 *pnetfid, int xid);
|
||||
void cifs_fill_uniqueid(struct super_block *sb, 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 *);
|
|||
extern int cifs_sign_smb2(struct kvec *iov, int n_vec, struct TCP_Server_Info *,
|
||||
__u32 *);
|
||||
extern int cifs_verify_signature(struct smb_hdr *,
|
||||
const struct mac_key *mac_key,
|
||||
const struct session_key *session_key,
|
||||
__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);
|
||||
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 *);
|
||||
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
||||
extern void calc_lanman_hash(const char *password, const char *cryptkey,
|
||||
|
@ -408,4 +408,8 @@ extern int CIFSSMBSetPosixACL(const int xid, struct cifsTconInfo *tcon,
|
|||
extern int CIFSGetExtAttr(const int xid, struct cifsTconInfo *tcon,
|
||||
const int netfid, __u64 *pExtAttrBits, __u64 *pMask);
|
||||
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 */
|
||||
|
|
|
@ -91,13 +91,13 @@ static void mark_open_files_invalid(struct cifsTconInfo *pTcon)
|
|||
struct list_head *tmp1;
|
||||
|
||||
/* 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) {
|
||||
open_file = list_entry(tmp, struct cifsFileInfo, tlist);
|
||||
open_file->invalidHandle = 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
|
||||
to this tcon */
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||
|
||||
if (rsp->EncryptionKeyLength ==
|
||||
cpu_to_le16(CIFS_CRYPTO_KEY_SIZE)) {
|
||||
memcpy(server->cryptKey, rsp->EncryptionKey,
|
||||
memcpy(ses->cryptKey, rsp->EncryptionKey,
|
||||
CIFS_CRYPTO_KEY_SIZE);
|
||||
} else if (server->secMode & SECMODE_PW_ENCRYPT) {
|
||||
rc = -EIO; /* need cryptkey unless plain text */
|
||||
|
@ -574,7 +574,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||
server->timeAdj = (int)(__s16)le16_to_cpu(pSMBr->ServerTimeZone);
|
||||
server->timeAdj *= 60;
|
||||
if (pSMBr->EncryptionKeyLength == CIFS_CRYPTO_KEY_SIZE) {
|
||||
memcpy(server->cryptKey, pSMBr->u.EncryptionKey,
|
||||
memcpy(ses->cryptKey, pSMBr->u.EncryptionKey,
|
||||
CIFS_CRYPTO_KEY_SIZE);
|
||||
} else if ((pSMBr->hdr.Flags2 & SMBFLG2_EXT_SEC)
|
||||
&& (pSMBr->EncryptionKeyLength == 0)) {
|
||||
|
@ -593,9 +593,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||
rc = -EIO;
|
||||
goto neg_err_exit;
|
||||
}
|
||||
read_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (server->srv_count > 1) {
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
if (memcmp(server->server_GUID,
|
||||
pSMBr->u.extended_response.
|
||||
GUID, 16) != 0) {
|
||||
|
@ -605,7 +605,7 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||
16);
|
||||
}
|
||||
} else {
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
memcpy(server->server_GUID,
|
||||
pSMBr->u.extended_response.GUID, 16);
|
||||
}
|
||||
|
@ -620,13 +620,15 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
|
|||
rc = 0;
|
||||
else
|
||||
rc = -EINVAL;
|
||||
|
||||
if (server->sec_kerberos || server->sec_mskerberos)
|
||||
server->secType = Kerberos;
|
||||
else if (server->sec_ntlmssp)
|
||||
server->secType = RawNTLMSSP;
|
||||
else
|
||||
rc = -EOPNOTSUPP;
|
||||
if (server->secType == Kerberos) {
|
||||
if (!server->sec_kerberos &&
|
||||
!server->sec_mskerberos)
|
||||
rc = -EOPNOTSUPP;
|
||||
} else if (server->secType == RawNTLMSSP) {
|
||||
if (!server->sec_ntlmssp)
|
||||
rc = -EOPNOTSUPP;
|
||||
} else
|
||||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
} else
|
||||
server->capabilities &= ~CAP_EXTENDED_SECURITY;
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* 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 @@
|
|||
#include "ntlmssp.h"
|
||||
#include "nterr.h"
|
||||
#include "rfc1002pdu.h"
|
||||
#include "cn_cifs.h"
|
||||
#include "fscache.h"
|
||||
|
||||
#define CIFS_PORT 445
|
||||
|
@ -100,16 +99,24 @@ struct smb_vol {
|
|||
bool noautotune:1;
|
||||
bool nostrictsync:1; /* do not force expensive SMBflush on every sync */
|
||||
bool fsc:1; /* enable fscache */
|
||||
bool mfsymlinks:1; /* use Minshall+French Symlinks */
|
||||
bool multiuser:1;
|
||||
unsigned int rsize;
|
||||
unsigned int wsize;
|
||||
bool sockopt_tcp_nodelay:1;
|
||||
unsigned short int port;
|
||||
char *prepath;
|
||||
struct sockaddr_storage srcaddr; /* allow binding to a local IP */
|
||||
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 ipv6_connect(struct TCP_Server_Info *server);
|
||||
static void cifs_prune_tlinks(struct work_struct *work);
|
||||
|
||||
/*
|
||||
* cifs tcp session reconnection
|
||||
|
@ -143,7 +150,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
|
||||
/* before reconnecting the tcp session, mark the smb session (uid)
|
||||
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) {
|
||||
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||
ses->need_reconnect = true;
|
||||
|
@ -153,7 +160,7 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
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 */
|
||||
mutex_lock(&server->srv_mutex);
|
||||
if (server->ssocket) {
|
||||
|
@ -166,6 +173,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
sock_release(server->ssocket);
|
||||
server->ssocket = NULL;
|
||||
}
|
||||
server->sequence_number = 0;
|
||||
server->session_estab = false;
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
list_for_each(tmp, &server->pending_mid_q) {
|
||||
|
@ -198,7 +207,6 @@ cifs_reconnect(struct TCP_Server_Info *server)
|
|||
spin_lock(&GlobalMid_Lock);
|
||||
if (server->tcpStatus != CifsExiting)
|
||||
server->tcpStatus = CifsGood;
|
||||
server->sequence_number = 0;
|
||||
spin_unlock(&GlobalMid_Lock);
|
||||
/* atomic_set(&server->inFlight,0);*/
|
||||
wake_up(&server->response_q);
|
||||
|
@ -629,9 +637,9 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
|||
} /* end while !EXITING */
|
||||
|
||||
/* 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);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
|
@ -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
|
||||
* 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)) {
|
||||
/* loop through server session structures attached to this and
|
||||
mark them dead */
|
||||
|
@ -679,7 +687,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
|||
ses->status = CifsExiting;
|
||||
ses->server = NULL;
|
||||
}
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
} else {
|
||||
/* although we can not zero the server struct pointer yet,
|
||||
since there are active requests which may depnd on them,
|
||||
|
@ -702,7 +710,7 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
|||
}
|
||||
}
|
||||
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 */
|
||||
msleep(125);
|
||||
}
|
||||
|
@ -725,12 +733,12 @@ cifs_demultiplex_thread(struct TCP_Server_Info *server)
|
|||
if a crazy root user tried to kill cifsd
|
||||
kernel thread explicitly this might happen) */
|
||||
/* 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) {
|
||||
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
|
||||
ses->server = NULL;
|
||||
}
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
kfree(server->hostname);
|
||||
task_to_wake = xchg(&server->tsk, NULL);
|
||||
|
@ -1046,6 +1054,22 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
"long\n");
|
||||
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) {
|
||||
if (!value || !*value) {
|
||||
printk(KERN_WARNING
|
||||
|
@ -1325,6 +1349,10 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
"/proc/fs/cifs/LookupCacheEnabled to 0\n");
|
||||
} else if (strnicmp(data, "fsc", 3) == 0) {
|
||||
vol->fsc = true;
|
||||
} else if (strnicmp(data, "mfsymlinks", 10) == 0) {
|
||||
vol->mfsymlinks = true;
|
||||
} else if (strnicmp(data, "multiuser", 8) == 0) {
|
||||
vol->multiuser = true;
|
||||
} else
|
||||
printk(KERN_WARNING "CIFS: Unknown mount option %s\n",
|
||||
data);
|
||||
|
@ -1356,6 +1384,13 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) {
|
||||
cERROR(1, "Multiuser mounts currently require krb5 "
|
||||
"authentication!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (vol->UNCip == NULL)
|
||||
vol->UNCip = &vol->UNC[2];
|
||||
|
||||
|
@ -1374,8 +1409,36 @@ cifs_parse_mount_options(char *options, const char *devname,
|
|||
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
|
||||
match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
|
||||
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
|
||||
match_address(struct TCP_Server_Info *server, struct sockaddr *addr,
|
||||
struct sockaddr *srcaddr)
|
||||
{
|
||||
struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
|
||||
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
|
||||
|
@ -1402,6 +1465,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1458,29 +1524,21 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
|
|||
{
|
||||
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) {
|
||||
/*
|
||||
* the demux thread can exit on its own while still in CifsNew
|
||||
* 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))
|
||||
if (!match_address(server, addr,
|
||||
(struct sockaddr *)&vol->srcaddr))
|
||||
continue;
|
||||
|
||||
if (!match_security(server, vol))
|
||||
continue;
|
||||
|
||||
++server->srv_count;
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cFYI(1, "Existing tcp session with server found");
|
||||
return server;
|
||||
}
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1489,14 +1547,14 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
|
|||
{
|
||||
struct task_struct *task;
|
||||
|
||||
write_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if (--server->srv_count > 0) {
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_del_init(&server->tcp_ses_list);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
|
@ -1574,6 +1632,7 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
|||
volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
|
||||
memcpy(tcp_ses->server_RFC1001_name,
|
||||
volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
|
||||
tcp_ses->session_estab = false;
|
||||
tcp_ses->sequence_number = 0;
|
||||
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
|
||||
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
|
||||
|
@ -1584,6 +1643,8 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
|||
* no need to spinlock this init of tcpStatus or srv_count
|
||||
*/
|
||||
tcp_ses->tcpStatus = CifsNew;
|
||||
memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
|
||||
sizeof(tcp_ses->srcaddr));
|
||||
++tcp_ses->srv_count;
|
||||
|
||||
if (addr.ss_family == AF_INET6) {
|
||||
|
@ -1618,9 +1679,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
|
|||
}
|
||||
|
||||
/* 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);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cifs_fscache_get_client_cookie(tcp_ses);
|
||||
|
||||
|
@ -1642,7 +1703,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
|
|||
{
|
||||
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) {
|
||||
switch (server->secType) {
|
||||
case Kerberos:
|
||||
|
@ -1662,10 +1723,10 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
|
|||
continue;
|
||||
}
|
||||
++ses->ses_count;
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return ses;
|
||||
}
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1676,14 +1737,14 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
|
|||
struct TCP_Server_Info *server = ses->server;
|
||||
|
||||
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) {
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_del_init(&ses->smb_ses_list);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
if (ses->status == CifsGood) {
|
||||
xid = GetXid();
|
||||
|
@ -1740,6 +1801,8 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
|
|||
if (ses == NULL)
|
||||
goto get_ses_fail;
|
||||
|
||||
ses->tilen = 0;
|
||||
ses->tiblob = NULL;
|
||||
/* new SMB session uses our server ref */
|
||||
ses->server = server;
|
||||
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)
|
|||
goto get_ses_fail;
|
||||
|
||||
/* 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);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
FreeXid(xid);
|
||||
return ses;
|
||||
|
@ -1797,7 +1860,7 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
|
|||
struct list_head *tmp;
|
||||
struct cifsTconInfo *tcon;
|
||||
|
||||
write_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each(tmp, &ses->tcon_list) {
|
||||
tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
|
||||
if (tcon->tidStatus == CifsExiting)
|
||||
|
@ -1806,10 +1869,10 @@ cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
|
|||
continue;
|
||||
|
||||
++tcon->tc_count;
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return tcon;
|
||||
}
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -1820,14 +1883,14 @@ cifs_put_tcon(struct cifsTconInfo *tcon)
|
|||
struct cifsSesInfo *ses = tcon->ses;
|
||||
|
||||
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) {
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
list_del_init(&tcon->tcon_list);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
xid = GetXid();
|
||||
CIFSSMBTDis(xid, tcon);
|
||||
|
@ -1900,9 +1963,9 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)
|
|||
tcon->nocase = volume_info->nocase;
|
||||
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);
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cifs_fscache_get_super_cookie(tcon);
|
||||
|
||||
|
@ -1913,6 +1976,23 @@ cifs_get_tcon(struct cifsSesInfo *ses, struct smb_vol *volume_info)
|
|||
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
|
||||
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)
|
|||
|
||||
}
|
||||
|
||||
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
|
||||
ipv4_connect(struct TCP_Server_Info *server)
|
||||
|
@ -2022,6 +2129,10 @@ ipv4_connect(struct TCP_Server_Info *server)
|
|||
cifs_reclassify_socket4(socket);
|
||||
}
|
||||
|
||||
rc = bind_socket(server);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* user overrode default port */
|
||||
if (server->addr.sockAddr.sin_port) {
|
||||
rc = socket->ops->connect(socket, (struct sockaddr *)
|
||||
|
@ -2184,6 +2295,10 @@ ipv6_connect(struct TCP_Server_Info *server)
|
|||
cifs_reclassify_socket6(socket);
|
||||
}
|
||||
|
||||
rc = bind_socket(server);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* user overrode default port */
|
||||
if (server->addr.sockAddr6.sin6_port) {
|
||||
rc = socket->ops->connect(socket,
|
||||
|
@ -2383,6 +2498,8 @@ convert_delimiter(char *path, char delim)
|
|||
static void setup_cifs_sb(struct smb_vol *pvolume_info,
|
||||
struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
|
||||
|
||||
if (pvolume_info->rsize > CIFSMaxBufSize) {
|
||||
cERROR(1, "rsize %d too large, using MaxBufSize",
|
||||
pvolume_info->rsize);
|
||||
|
@ -2462,10 +2579,21 @@ static void setup_cifs_sb(struct smb_vol *pvolume_info,
|
|||
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
|
||||
if (pvolume_info->fsc)
|
||||
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) {
|
||||
cFYI(1, "mounting share using direct i/o");
|
||||
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))
|
||||
cERROR(1, "mount option dynperm ignored if cifsacl "
|
||||
|
@ -2552,6 +2680,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
struct TCP_Server_Info *srvTcp;
|
||||
char *full_path;
|
||||
char *mount_data = mount_data_global;
|
||||
struct tcon_link *tlink;
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
struct dfs_info3_param *referrals = NULL;
|
||||
unsigned int num_referrals = 0;
|
||||
|
@ -2563,6 +2692,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
pSesInfo = NULL;
|
||||
srvTcp = NULL;
|
||||
full_path = NULL;
|
||||
tlink = NULL;
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
|
@ -2638,8 +2768,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
goto remote_path_check;
|
||||
}
|
||||
|
||||
cifs_sb->tcon = tcon;
|
||||
|
||||
/* do not care if following two calls succeed - informational */
|
||||
if (!tcon->ipc) {
|
||||
CIFSSMBQFSDeviceInfo(xid, tcon);
|
||||
|
@ -2748,6 +2876,38 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
|
|||
#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:
|
||||
/* on error free sesinfo and tcon struct if needed */
|
||||
if (rc) {
|
||||
|
@ -2825,14 +2985,13 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
|
|||
#ifdef CONFIG_CIFS_WEAK_PW_HASH
|
||||
if ((global_secflags & CIFSSEC_MAY_LANMAN) &&
|
||||
(ses->server->secType == LANMAN))
|
||||
calc_lanman_hash(tcon->password, ses->server->cryptKey,
|
||||
calc_lanman_hash(tcon->password, ses->cryptKey,
|
||||
ses->server->secMode &
|
||||
SECMODE_PW_ENCRYPT ? true : false,
|
||||
bcc_ptr);
|
||||
else
|
||||
#endif /* CIFS_WEAK_PW_HASH */
|
||||
SMBNTencrypt(tcon->password, ses->server->cryptKey,
|
||||
bcc_ptr);
|
||||
SMBNTencrypt(tcon->password, ses->cryptKey, bcc_ptr);
|
||||
|
||||
bcc_ptr += CIFS_SESS_KEY_SIZE;
|
||||
if (ses->capabilities & CAP_UNICODE) {
|
||||
|
@ -2934,19 +3093,39 @@ CIFSTCon(unsigned int xid, struct cifsSesInfo *ses,
|
|||
int
|
||||
cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
|
||||
{
|
||||
int rc = 0;
|
||||
int i, ret;
|
||||
char *tmp;
|
||||
struct tcon_link *tlink[8];
|
||||
unsigned long index = 0;
|
||||
|
||||
if (cifs_sb->tcon)
|
||||
cifs_put_tcon(cifs_sb->tcon);
|
||||
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);
|
||||
|
||||
cifs_sb->tcon = NULL;
|
||||
tmp = cifs_sb->prepath;
|
||||
cifs_sb->prepathlen = 0;
|
||||
cifs_sb->prepath = NULL;
|
||||
kfree(tmp);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cifs_negotiate_protocol(unsigned int xid, struct cifsSesInfo *ses)
|
||||
|
@ -2997,6 +3176,15 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses,
|
|||
if (rc) {
|
||||
cERROR(1, "Send error in SessSetup = %d", rc);
|
||||
} 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");
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
ses->status = CifsGood;
|
||||
|
@ -3007,3 +3195,237 @@ int cifs_setup_session(unsigned int xid, struct cifsSesInfo *ses,
|
|||
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);
|
||||
}
|
||||
|
|
214
fs/cifs/dir.c
214
fs/cifs/dir.c
|
@ -54,18 +54,18 @@ build_path_from_dentry(struct dentry *direntry)
|
|||
int dfsplen;
|
||||
char *full_path;
|
||||
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)
|
||||
return NULL; /* not much we can do if dentry is freed and
|
||||
we need to reopen the file after it was closed implicitly
|
||||
when the server crashed */
|
||||
|
||||
cifs_sb = CIFS_SB(direntry->d_sb);
|
||||
dirsep = CIFS_DIR_SEP(cifs_sb);
|
||||
pplen = cifs_sb->prepathlen;
|
||||
if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
|
||||
dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
|
||||
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
|
||||
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
|
||||
else
|
||||
dfsplen = 0;
|
||||
cifs_bp_rename_retry:
|
||||
|
@ -117,7 +117,7 @@ build_path_from_dentry(struct dentry *direntry)
|
|||
/* BB test paths to Windows with '/' in the midst of prepath */
|
||||
|
||||
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) {
|
||||
int i;
|
||||
for (i = 0; i < dfsplen; i++) {
|
||||
|
@ -130,135 +130,6 @@ build_path_from_dentry(struct dentry *direntry)
|
|||
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,
|
||||
struct dentry *direntry,
|
||||
struct inode *newinode)
|
||||
|
@ -291,6 +162,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
int desiredAccess = GENERIC_READ | GENERIC_WRITE;
|
||||
__u16 fileHandle;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *tcon;
|
||||
char *full_path = NULL;
|
||||
FILE_ALL_INFO *buf = NULL;
|
||||
|
@ -300,7 +172,20 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
xid = GetXid();
|
||||
|
||||
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);
|
||||
|
||||
if (oplockEnabled)
|
||||
oplock = REQ_OPLOCK;
|
||||
|
||||
if (nd && (nd->flags & LOOKUP_OPEN))
|
||||
oflags = nd->intent.open.file->f_flags;
|
||||
else
|
||||
oflags = O_RDONLY | O_CREAT;
|
||||
|
||||
full_path = build_path_from_dentry(direntry);
|
||||
if (full_path == NULL) {
|
||||
|
@ -308,14 +193,6 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
goto cifs_create_out;
|
||||
}
|
||||
|
||||
if (oplockEnabled)
|
||||
oplock = REQ_OPLOCK;
|
||||
|
||||
if (nd && (nd->flags & LOOKUP_OPEN))
|
||||
oflags = nd->intent.open.flags;
|
||||
else
|
||||
oflags = FMODE_READ | SMB_O_CREAT;
|
||||
|
||||
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
|
||||
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
|
||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||
|
@ -344,9 +221,9 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
/* if the file is going to stay open, then we
|
||||
need to set the desired access properly */
|
||||
desiredAccess = 0;
|
||||
if (oflags & FMODE_READ)
|
||||
if (OPEN_FMODE(oflags) & FMODE_READ)
|
||||
desiredAccess |= GENERIC_READ; /* is this too little? */
|
||||
if (oflags & FMODE_WRITE)
|
||||
if (OPEN_FMODE(oflags) & FMODE_WRITE)
|
||||
desiredAccess |= GENERIC_WRITE;
|
||||
|
||||
if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
|
||||
|
@ -375,7 +252,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
if (!tcon->unix_ext && (mode & S_IWUGO) == 0)
|
||||
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,
|
||||
desiredAccess, create_options,
|
||||
&fileHandle, &oplock, buf, cifs_sb->local_nls,
|
||||
|
@ -467,8 +344,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
goto cifs_create_out;
|
||||
}
|
||||
|
||||
pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp,
|
||||
nd->path.mnt, oflags);
|
||||
pfile_info = cifs_new_fileinfo(fileHandle, filp, tlink, oplock);
|
||||
if (pfile_info == NULL) {
|
||||
fput(filp);
|
||||
CIFSSMBClose(xid, tcon, fileHandle);
|
||||
|
@ -481,6 +357,7 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
cifs_create_out:
|
||||
kfree(buf);
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
@ -491,6 +368,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
|
|||
int rc = -EPERM;
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
char *full_path = NULL;
|
||||
struct inode *newinode = NULL;
|
||||
|
@ -503,10 +381,14 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
|
|||
if (!old_valid_dev(device_number))
|
||||
return -EINVAL;
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
|
@ -606,6 +488,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, int mode,
|
|||
kfree(full_path);
|
||||
kfree(buf);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -619,6 +502,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
__u16 fileHandle = 0;
|
||||
bool posix_open = false;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct cifsFileInfo *cfile;
|
||||
struct inode *newInode = NULL;
|
||||
|
@ -633,7 +517,12 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
/* check whether path exists */
|
||||
|
||||
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.
|
||||
|
@ -644,8 +533,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
for (i = 0; i < direntry->d_name.len; i++)
|
||||
if (direntry->d_name.name[i] == '\\') {
|
||||
cFYI(1, "Invalid file name");
|
||||
FreeXid(xid);
|
||||
return ERR_PTR(-EINVAL);
|
||||
rc = -EINVAL;
|
||||
goto lookup_out;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -655,7 +544,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
*/
|
||||
if (nd && (nd->flags & LOOKUP_EXCL)) {
|
||||
d_instantiate(direntry, NULL);
|
||||
return NULL;
|
||||
rc = 0;
|
||||
goto lookup_out;
|
||||
}
|
||||
|
||||
/* can not grab the rename sem here since it would
|
||||
|
@ -663,8 +553,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
in which we already have the sb rename sem */
|
||||
full_path = build_path_from_dentry(direntry);
|
||||
if (full_path == NULL) {
|
||||
FreeXid(xid);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
rc = -ENOMEM;
|
||||
goto lookup_out;
|
||||
}
|
||||
|
||||
if (direntry->d_inode != NULL) {
|
||||
|
@ -687,11 +577,11 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
if (pTcon->unix_ext) {
|
||||
if (nd && !(nd->flags & (LOOKUP_PARENT | LOOKUP_DIRECTORY)) &&
|
||||
(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,
|
||||
parent_dir_inode->i_sb,
|
||||
nd->intent.open.create_mode,
|
||||
nd->intent.open.flags, &oplock,
|
||||
nd->intent.open.file->f_flags, &oplock,
|
||||
&fileHandle, xid);
|
||||
/*
|
||||
* The check below works around a bug in POSIX
|
||||
|
@ -727,9 +617,8 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
goto lookup_out;
|
||||
}
|
||||
|
||||
cfile = cifs_new_fileinfo(newInode, fileHandle, filp,
|
||||
nd->path.mnt,
|
||||
nd->intent.open.flags);
|
||||
cfile = cifs_new_fileinfo(fileHandle, filp, tlink,
|
||||
oplock);
|
||||
if (cfile == NULL) {
|
||||
fput(filp);
|
||||
CIFSSMBClose(xid, pTcon, fileHandle);
|
||||
|
@ -759,6 +648,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
|
||||
lookup_out:
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
FreeXid(xid);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
|
797
fs/cifs/file.c
797
fs/cifs/file.c
File diff suppressed because it is too large
Load diff
|
@ -62,15 +62,15 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode)
|
|||
{
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(inode);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
if (cifsi->fscache)
|
||||
return;
|
||||
|
||||
cifsi->fscache = fscache_acquire_cookie(cifs_sb->tcon->fscache,
|
||||
&cifs_fscache_inode_object_def,
|
||||
cifsi);
|
||||
cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)",
|
||||
cifs_sb->tcon->fscache, cifsi->fscache);
|
||||
cifsi->fscache = fscache_acquire_cookie(tcon->fscache,
|
||||
&cifs_fscache_inode_object_def, cifsi);
|
||||
cFYI(1, "CIFS: got FH cookie (0x%p/0x%p)", tcon->fscache,
|
||||
cifsi->fscache);
|
||||
}
|
||||
|
||||
void cifs_fscache_release_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 */
|
||||
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,
|
||||
cifsi);
|
||||
cFYI(1, "CIFS: new cookie 0x%p oldcookie 0x%p",
|
||||
|
|
237
fs/cifs/inode.c
237
fs/cifs/inode.c
|
@ -52,7 +52,7 @@ static void cifs_set_ops(struct inode *inode, const bool is_dfs_referral)
|
|||
|
||||
|
||||
/* 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)
|
||||
inode->i_data.a_ops = &cifs_addr_ops_smallbuf;
|
||||
else
|
||||
|
@ -288,8 +288,8 @@ int cifs_get_file_info_unix(struct file *filp)
|
|||
struct cifs_fattr fattr;
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct cifsTconInfo *tcon = cifs_sb->tcon;
|
||||
struct cifsFileInfo *cfile = filp->private_data;
|
||||
struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
xid = GetXid();
|
||||
rc = CIFSSMBUnixQFileInfo(xid, tcon, cfile->netfid, &find_data);
|
||||
|
@ -313,15 +313,21 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
|||
FILE_UNIX_BASIC_INFO find_data;
|
||||
struct cifs_fattr fattr;
|
||||
struct cifsTconInfo *tcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
|
||||
tcon = cifs_sb->tcon;
|
||||
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 */
|
||||
rc = CIFSSMBUnixQPathInfo(xid, tcon, full_path, &find_data,
|
||||
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
if (!rc) {
|
||||
cifs_unix_basic_to_fattr(&fattr, &find_data, cifs_sb);
|
||||
|
@ -332,6 +338,13 @@ int cifs_get_inode_info_unix(struct inode **pinode,
|
|||
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) {
|
||||
/* get new inode */
|
||||
cifs_fill_uniqueid(sb, &fattr);
|
||||
|
@ -353,7 +366,8 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
|
|||
int rc;
|
||||
int oplock = 0;
|
||||
__u16 netfid;
|
||||
struct cifsTconInfo *pTcon = cifs_sb->tcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *tcon;
|
||||
char buf[24];
|
||||
unsigned int bytes_read;
|
||||
char *pbuf;
|
||||
|
@ -372,7 +386,12 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
|
|||
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,
|
||||
cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
|
@ -380,7 +399,7 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
|
|||
if (rc == 0) {
|
||||
int buf_type = CIFS_NO_BUFFER;
|
||||
/* Read header */
|
||||
rc = CIFSSMBRead(xid, pTcon, netfid,
|
||||
rc = CIFSSMBRead(xid, tcon, netfid,
|
||||
24 /* length */, 0 /* offset */,
|
||||
&bytes_read, &pbuf, &buf_type);
|
||||
if ((rc == 0) && (bytes_read >= 8)) {
|
||||
|
@ -422,8 +441,9 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,
|
|||
fattr->cf_dtype = DT_REG;
|
||||
rc = -EOPNOTSUPP; /* or some unknown SFU type */
|
||||
}
|
||||
CIFSSMBClose(xid, pTcon, netfid);
|
||||
CIFSSMBClose(xid, tcon, netfid);
|
||||
}
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -441,11 +461,19 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
|
|||
ssize_t rc;
|
||||
char ea_value[4];
|
||||
__u32 mode;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *tcon;
|
||||
|
||||
rc = CIFSSMBQAllEAs(xid, cifs_sb->tcon, path, "SETFILEBITS",
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
tcon = tlink_tcon(tlink);
|
||||
|
||||
rc = CIFSSMBQAllEAs(xid, tcon, path, "SETFILEBITS",
|
||||
ea_value, 4 /* size of buf */, cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
cifs_put_tlink(tlink);
|
||||
if (rc < 0)
|
||||
return (int)rc;
|
||||
else if (rc > 3) {
|
||||
|
@ -468,6 +496,8 @@ static void
|
|||
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
||||
struct cifs_sb_info *cifs_sb, bool adjust_tz)
|
||||
{
|
||||
struct cifsTconInfo *tcon = cifs_sb_master_tcon(cifs_sb);
|
||||
|
||||
memset(fattr, 0, sizeof(*fattr));
|
||||
fattr->cf_cifsattrs = le32_to_cpu(info->Attributes);
|
||||
if (info->DeletePending)
|
||||
|
@ -482,8 +512,8 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
|
|||
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
|
||||
|
||||
if (adjust_tz) {
|
||||
fattr->cf_ctime.tv_sec += cifs_sb->tcon->ses->server->timeAdj;
|
||||
fattr->cf_mtime.tv_sec += cifs_sb->tcon->ses->server->timeAdj;
|
||||
fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
|
||||
fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
|
||||
}
|
||||
|
||||
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
|
||||
|
@ -515,8 +545,8 @@ int cifs_get_file_info(struct file *filp)
|
|||
struct cifs_fattr fattr;
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
|
||||
struct cifsTconInfo *tcon = cifs_sb->tcon;
|
||||
struct cifsFileInfo *cfile = filp->private_data;
|
||||
struct cifsTconInfo *tcon = tlink_tcon(cfile->tlink);
|
||||
|
||||
xid = GetXid();
|
||||
rc = CIFSSMBQFileInfo(xid, tcon, cfile->netfid, &find_data);
|
||||
|
@ -554,26 +584,33 @@ int cifs_get_inode_info(struct inode **pinode,
|
|||
{
|
||||
int rc = 0, tmprc;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
char *buf = NULL;
|
||||
bool adjustTZ = false;
|
||||
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);
|
||||
|
||||
if ((pfindData == NULL) && (*pinode != NULL)) {
|
||||
if (CIFS_I(*pinode)->clientCanCacheRead) {
|
||||
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 (pfindData == NULL) {
|
||||
buf = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL);
|
||||
if (buf == NULL)
|
||||
return -ENOMEM;
|
||||
if (buf == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto cgii_exit;
|
||||
}
|
||||
pfindData = (FILE_ALL_INFO *)buf;
|
||||
|
||||
/* could do find first instead but this returns more info */
|
||||
|
@ -661,6 +698,13 @@ int cifs_get_inode_info(struct inode **pinode,
|
|||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL)
|
||||
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) {
|
||||
*pinode = cifs_iget(sb, &fattr);
|
||||
if (!*pinode)
|
||||
|
@ -671,6 +715,7 @@ int cifs_get_inode_info(struct inode **pinode,
|
|||
|
||||
cgii_exit:
|
||||
kfree(buf);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -683,6 +728,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
|
|||
int pplen = cifs_sb->prepathlen;
|
||||
int dfsplen;
|
||||
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 (pplen == 0) {
|
||||
|
@ -692,8 +738,8 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
|
|||
return full_path;
|
||||
}
|
||||
|
||||
if (cifs_sb->tcon && (cifs_sb->tcon->Flags & SMB_SHARE_IS_IN_DFS))
|
||||
dfsplen = strnlen(cifs_sb->tcon->treeName, MAX_TREE_SIZE + 1);
|
||||
if (tcon->Flags & SMB_SHARE_IS_IN_DFS)
|
||||
dfsplen = strnlen(tcon->treeName, MAX_TREE_SIZE + 1);
|
||||
else
|
||||
dfsplen = 0;
|
||||
|
||||
|
@ -702,7 +748,7 @@ char *cifs_build_path_to_root(struct cifs_sb_info *cifs_sb)
|
|||
return full_path;
|
||||
|
||||
if (dfsplen) {
|
||||
strncpy(full_path, cifs_sb->tcon->treeName, dfsplen);
|
||||
strncpy(full_path, tcon->treeName, dfsplen);
|
||||
/* switch slash direction in prepath depending on whether
|
||||
* windows or posix style path names
|
||||
*/
|
||||
|
@ -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)
|
||||
{
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct inode *inode = NULL;
|
||||
long rc;
|
||||
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);
|
||||
if (full_path == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
xid = GetXid();
|
||||
if (cifs_sb->tcon->unix_ext)
|
||||
if (tcon->unix_ext)
|
||||
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
|
||||
else
|
||||
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)
|
|||
|
||||
#ifdef CONFIG_CIFS_FSCACHE
|
||||
/* populate tcon->resource_id */
|
||||
cifs_sb->tcon->resource_id = CIFS_I(inode)->uniqueid;
|
||||
tcon->resource_id = CIFS_I(inode)->uniqueid;
|
||||
#endif
|
||||
|
||||
if (rc && cifs_sb->tcon->ipc) {
|
||||
if (rc && tcon->ipc) {
|
||||
cFYI(1, "ipc connection - fake read inode");
|
||||
inode->i_mode |= S_IFDIR;
|
||||
inode->i_nlink = 2;
|
||||
|
@ -879,7 +925,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
|
|||
struct cifsFileInfo *open_file;
|
||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||
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;
|
||||
|
||||
if (attrs == NULL)
|
||||
|
@ -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
|
||||
*/
|
||||
open_file = find_writable_file(cifsInode);
|
||||
open_file = find_writable_file(cifsInode, true);
|
||||
if (open_file) {
|
||||
netfid = open_file->netfid;
|
||||
netpid = open_file->pid;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
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
|
||||
* really work.
|
||||
|
@ -968,6 +1024,8 @@ cifs_set_file_info(struct inode *inode, struct iattr *attrs, int xid,
|
|||
else
|
||||
cifsFileInfo_put(open_file);
|
||||
out:
|
||||
if (tlink != NULL)
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -985,10 +1043,16 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
|
|||
struct inode *inode = dentry->d_inode;
|
||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||
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;
|
||||
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,
|
||||
DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR,
|
||||
&netfid, &oplock, NULL, cifs_sb->local_nls,
|
||||
|
@ -1057,6 +1121,7 @@ cifs_rename_pending_delete(char *full_path, struct dentry *dentry, int xid)
|
|||
CIFSSMBClose(xid, tcon, netfid);
|
||||
out:
|
||||
kfree(info_buf);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
|
||||
/*
|
||||
|
@ -1096,12 +1161,18 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
struct cifsInodeInfo *cifs_inode;
|
||||
struct super_block *sb = dir->i_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;
|
||||
__u32 dosattr = 0, origattr = 0;
|
||||
|
||||
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();
|
||||
|
||||
/* 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)
|
|||
full_path = build_path_from_dentry(dentry);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto unlink_out;
|
||||
}
|
||||
|
||||
if ((tcon->ses->capabilities & CAP_UNIX) &&
|
||||
|
@ -1176,10 +1246,11 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
dir->i_ctime = dir->i_mtime = current_fs_time(sb);
|
||||
cifs_inode = CIFS_I(dir);
|
||||
CIFS_I(dir)->time = 0; /* force revalidate of dir as well */
|
||||
|
||||
unlink_out:
|
||||
kfree(full_path);
|
||||
kfree(attrs);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1188,6 +1259,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
|||
int rc = 0, tmprc;
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
char *full_path = NULL;
|
||||
struct inode *newinode = NULL;
|
||||
|
@ -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);
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto mkdir_out;
|
||||
}
|
||||
|
||||
if ((pTcon->ses->capabilities & CAP_UNIX) &&
|
||||
|
@ -1362,6 +1436,7 @@ int cifs_mkdir(struct inode *inode, struct dentry *direntry, int mode)
|
|||
mkdir_out:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1370,6 +1445,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
|
|||
int rc = 0;
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
char *full_path = NULL;
|
||||
struct cifsInodeInfo *cifsInode;
|
||||
|
@ -1378,18 +1454,23 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
|
|||
|
||||
xid = GetXid();
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
full_path = build_path_from_dentry(direntry);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto rmdir_exit;
|
||||
}
|
||||
|
||||
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,
|
||||
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
if (!rc) {
|
||||
drop_nlink(inode);
|
||||
|
@ -1410,6 +1491,7 @@ int cifs_rmdir(struct inode *inode, struct dentry *direntry)
|
|||
direntry->d_inode->i_ctime = inode->i_ctime = inode->i_mtime =
|
||||
current_fs_time(inode->i_sb);
|
||||
|
||||
rmdir_exit:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
|
@ -1420,10 +1502,16 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
|
|||
struct dentry *to_dentry, const char *toPath)
|
||||
{
|
||||
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;
|
||||
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 */
|
||||
rc = CIFSSMBRename(xid, pTcon, fromPath, toPath, cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
|
@ -1435,11 +1523,11 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
|
|||
* rename by filehandle to various Windows servers.
|
||||
*/
|
||||
if (rc == 0 || rc != -ETXTBSY)
|
||||
return rc;
|
||||
goto do_rename_exit;
|
||||
|
||||
/* open-file renames don't work across directories */
|
||||
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 */
|
||||
rc = CIFSSMBOpen(xid, pTcon, fromPath, FILE_OPEN, DELETE,
|
||||
|
@ -1455,7 +1543,8 @@ cifs_do_rename(int xid, struct dentry *from_dentry, const char *fromPath,
|
|||
|
||||
CIFSSMBClose(xid, pTcon, srcfid);
|
||||
}
|
||||
|
||||
do_rename_exit:
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1465,13 +1554,17 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
|||
char *fromName = NULL;
|
||||
char *toName = NULL;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *tcon;
|
||||
FILE_UNIX_BASIC_INFO *info_buf_source = NULL;
|
||||
FILE_UNIX_BASIC_INFO *info_buf_target;
|
||||
int xid, rc, tmprc;
|
||||
|
||||
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();
|
||||
|
||||
|
@ -1547,6 +1640,7 @@ int cifs_rename(struct inode *source_dir, struct dentry *source_dentry,
|
|||
kfree(fromName);
|
||||
kfree(toName);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -1599,11 +1693,12 @@ int cifs_revalidate_file(struct file *filp)
|
|||
{
|
||||
int rc = 0;
|
||||
struct inode *inode = filp->f_path.dentry->d_inode;
|
||||
struct cifsFileInfo *cfile = (struct cifsFileInfo *) filp->private_data;
|
||||
|
||||
if (!cifs_inode_needs_reval(inode))
|
||||
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);
|
||||
else
|
||||
rc = cifs_get_file_info(filp);
|
||||
|
@ -1644,7 +1739,7 @@ int cifs_revalidate_dentry(struct dentry *dentry)
|
|||
"jiffies %ld", full_path, inode, inode->i_count.counter,
|
||||
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);
|
||||
else
|
||||
rc = cifs_get_inode_info(&inode, full_path, NULL, sb,
|
||||
|
@ -1660,13 +1755,29 @@ int cifs_revalidate_dentry(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);
|
||||
|
||||
if (!err) {
|
||||
generic_fillattr(dentry->d_inode, stat);
|
||||
stat->blksize = CIFS_MAX_MSGSIZE;
|
||||
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;
|
||||
}
|
||||
|
@ -1708,7 +1819,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
|||
struct cifsFileInfo *open_file;
|
||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||
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
|
||||
|
@ -1719,10 +1831,11 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
|||
* writebehind data than the SMB timeout for the SetPathInfo
|
||||
* request would allow
|
||||
*/
|
||||
open_file = find_writable_file(cifsInode);
|
||||
open_file = find_writable_file(cifsInode, true);
|
||||
if (open_file) {
|
||||
__u16 nfid = open_file->netfid;
|
||||
__u32 npid = open_file->pid;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
rc = CIFSSMBSetFileSize(xid, pTcon, attrs->ia_size, nfid,
|
||||
npid, false);
|
||||
cifsFileInfo_put(open_file);
|
||||
|
@ -1737,6 +1850,13 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
|||
rc = -EINVAL;
|
||||
|
||||
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
|
||||
either because no valid, writeable file handle for
|
||||
it was found or because there was an error setting
|
||||
|
@ -1766,6 +1886,8 @@ cifs_set_file_size(struct inode *inode, struct iattr *attrs,
|
|||
CIFSSMBClose(xid, pTcon, netfid);
|
||||
}
|
||||
}
|
||||
if (tlink)
|
||||
cifs_put_tlink(tlink);
|
||||
}
|
||||
|
||||
if (rc == 0) {
|
||||
|
@ -1786,7 +1908,8 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
|||
struct inode *inode = direntry->d_inode;
|
||||
struct cifsInodeInfo *cifsInode = CIFS_I(inode);
|
||||
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 cifsFileInfo *open_file;
|
||||
|
||||
|
@ -1873,17 +1996,25 @@ cifs_setattr_unix(struct dentry *direntry, struct iattr *attrs)
|
|||
args->ctime = NO_CHANGE_64;
|
||||
|
||||
args->device = 0;
|
||||
open_file = find_writable_file(cifsInode);
|
||||
open_file = find_writable_file(cifsInode, true);
|
||||
if (open_file) {
|
||||
u16 nfid = open_file->netfid;
|
||||
u32 npid = open_file->pid;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
rc = CIFSSMBUnixSetFileInfo(xid, pTcon, args, nfid, npid);
|
||||
cifsFileInfo_put(open_file);
|
||||
} 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,
|
||||
cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
cifs_put_tlink(tlink);
|
||||
}
|
||||
|
||||
if (rc)
|
||||
|
@ -2064,7 +2195,7 @@ cifs_setattr(struct dentry *direntry, struct iattr *attrs)
|
|||
{
|
||||
struct inode *inode = direntry->d_inode;
|
||||
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)
|
||||
return cifs_setattr_unix(direntry, attrs);
|
||||
|
|
|
@ -37,11 +37,11 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
#ifdef CONFIG_CIFS_POSIX
|
||||
struct cifsFileInfo *pSMBFile = filep->private_data;
|
||||
struct cifsTconInfo *tcon = tlink_tcon(pSMBFile->tlink);
|
||||
__u64 ExtAttrBits = 0;
|
||||
__u64 ExtAttrMask = 0;
|
||||
__u64 caps;
|
||||
struct cifsTconInfo *tcon;
|
||||
struct cifsFileInfo *pSMBFile = filep->private_data;
|
||||
__u64 caps = le64_to_cpu(tcon->fsUnixInfo.Capability);
|
||||
#endif /* CONFIG_CIFS_POSIX */
|
||||
|
||||
xid = GetXid();
|
||||
|
@ -50,17 +50,6 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
|
|||
|
||||
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) {
|
||||
case CIFS_IOC_CHECKUMOUNT:
|
||||
cFYI(1, "User unmount attempted");
|
||||
|
|
372
fs/cifs/link.c
372
fs/cifs/link.c
|
@ -28,6 +28,296 @@
|
|||
#include "cifsproto.h"
|
||||
#include "cifs_debug.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
|
||||
cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
||||
|
@ -37,18 +327,18 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
|||
int xid;
|
||||
char *fromName = 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 cifsInodeInfo *cifsInode;
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
pTcon = tlink_tcon(tlink);
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
cifs_sb_target = CIFS_SB(inode->i_sb);
|
||||
pTcon = cifs_sb_target->tcon;
|
||||
|
||||
/* No need to check for cross device links since server will do that
|
||||
BB note DFS case in future though (when we may have to check) */
|
||||
|
||||
fromName = build_path_from_dentry(old_file);
|
||||
toName = build_path_from_dentry(direntry);
|
||||
if ((fromName == NULL) || (toName == NULL)) {
|
||||
|
@ -56,16 +346,15 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
|||
goto cifs_hl_exit;
|
||||
}
|
||||
|
||||
/* if (cifs_sb_target->tcon->ses->capabilities & CAP_UNIX)*/
|
||||
if (pTcon->unix_ext)
|
||||
rc = CIFSUnixCreateHardLink(xid, pTcon, fromName, toName,
|
||||
cifs_sb_target->local_nls,
|
||||
cifs_sb_target->mnt_cifs_flags &
|
||||
cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
else {
|
||||
rc = CIFSCreateHardLink(xid, pTcon, fromName, toName,
|
||||
cifs_sb_target->local_nls,
|
||||
cifs_sb_target->mnt_cifs_flags &
|
||||
cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
if ((rc == -EIO) || (rc == -EINVAL))
|
||||
rc = -EOPNOTSUPP;
|
||||
|
@ -101,6 +390,7 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,
|
|||
kfree(fromName);
|
||||
kfree(toName);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -113,10 +403,19 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
|
|||
char *full_path = NULL;
|
||||
char *target_path = NULL;
|
||||
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();
|
||||
|
||||
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.
|
||||
* Eventually we should handle NTFS reparse points, and MacOS
|
||||
|
@ -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
|
||||
* 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;
|
||||
goto out;
|
||||
}
|
||||
|
@ -141,8 +441,21 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
|
|||
|
||||
cFYI(1, "Full path: %s inode = 0x%p", full_path, inode);
|
||||
|
||||
rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path,
|
||||
cifs_sb->local_nls);
|
||||
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,
|
||||
cifs_sb->local_nls);
|
||||
|
||||
kfree(full_path);
|
||||
out:
|
||||
if (rc != 0) {
|
||||
|
@ -151,6 +464,8 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)
|
|||
}
|
||||
|
||||
FreeXid(xid);
|
||||
if (tlink)
|
||||
cifs_put_tlink(tlink);
|
||||
nd_set_link(nd, target_path);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -160,29 +475,37 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
|
|||
{
|
||||
int rc = -EOPNOTSUPP;
|
||||
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;
|
||||
char *full_path = NULL;
|
||||
struct inode *newinode = NULL;
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink)) {
|
||||
rc = PTR_ERR(tlink);
|
||||
goto symlink_exit;
|
||||
}
|
||||
pTcon = tlink_tcon(tlink);
|
||||
|
||||
full_path = build_path_from_dentry(direntry);
|
||||
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto symlink_exit;
|
||||
}
|
||||
|
||||
cFYI(1, "Full path: %s", full_path);
|
||||
cFYI(1, "symname is %s", symname);
|
||||
|
||||
/* 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,
|
||||
cifs_sb->local_nls);
|
||||
/* else
|
||||
|
@ -208,8 +531,9 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)
|
|||
d_instantiate(direntry, newinode);
|
||||
}
|
||||
}
|
||||
|
||||
symlink_exit:
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
@ -347,7 +347,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
|
|||
if (current_fsuid() != treeCon->ses->linux_uid) {
|
||||
cFYI(1, "Multiuser mode and 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) {
|
||||
ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
|
||||
if (ses->linux_uid == current_fsuid()) {
|
||||
|
@ -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)
|
|||
return false;
|
||||
|
||||
/* 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) {
|
||||
ses = list_entry(tmp, struct cifsSesInfo, smb_ses_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)
|
|||
continue;
|
||||
|
||||
cifs_stats_inc(&tcon->num_oplock_brks);
|
||||
read_lock(&GlobalSMBSeslock);
|
||||
spin_lock(&cifs_file_list_lock);
|
||||
list_for_each(tmp2, &tcon->openFileList) {
|
||||
netfile = list_entry(tmp2, struct cifsFileInfo,
|
||||
tlist);
|
||||
if (pSMB->Fid != netfile->netfid)
|
||||
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");
|
||||
pCifsInode = CIFS_I(netfile->pInode);
|
||||
pCifsInode = CIFS_I(netfile->dentry->d_inode);
|
||||
pCifsInode->clientCanCacheAll = false;
|
||||
if (pSMB->OplockLevel == 0)
|
||||
pCifsInode->clientCanCacheRead = false;
|
||||
|
@ -594,17 +584,17 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
|
|||
cifs_oplock_break_get(netfile);
|
||||
netfile->oplock_break_cancelled = false;
|
||||
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
return true;
|
||||
}
|
||||
read_unlock(&GlobalSMBSeslock);
|
||||
read_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
cFYI(1, "No matching file for oplock break");
|
||||
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");
|
||||
return true;
|
||||
}
|
||||
|
@ -729,6 +719,6 @@ cifs_autodisable_serverino(struct cifs_sb_info *cifs_sb)
|
|||
"properly. Hardlinks will not be recognized on this "
|
||||
"mount. Consider mounting with the \"noserverino\" "
|
||||
"option to silence this message.",
|
||||
cifs_sb->tcon->treeName);
|
||||
cifs_sb_master_tcon(cifs_sb)->treeName);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,6 +61,21 @@
|
|||
#define NTLMSSP_NEGOTIATE_KEY_XCH 0x40000000
|
||||
#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 */
|
||||
/* in the Linux kernel, in this particular case they are useful */
|
||||
/* to more closely match the standards document for NTLMSSP from */
|
||||
|
|
|
@ -102,7 +102,7 @@ cifs_readdir_lookup(struct dentry *parent, struct qstr *name,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (CIFS_SB(sb)->tcon->nocase)
|
||||
if (cifs_sb_master_tcon(CIFS_SB(sb))->nocase)
|
||||
dentry->d_op = &cifs_ci_dentry_ops;
|
||||
else
|
||||
dentry->d_op = &cifs_dentry_ops;
|
||||
|
@ -171,7 +171,7 @@ static void
|
|||
cifs_std_info_to_fattr(struct cifs_fattr *fattr, FIND_FILE_STANDARD_INFO *info,
|
||||
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));
|
||||
fattr->cf_atime = cnvrtDosUnixTm(info->LastAccessDate,
|
||||
|
@ -199,7 +199,7 @@ int get_symlink_reparse_path(char *full_path, struct cifs_sb_info *cifs_sb,
|
|||
int len;
|
||||
int oplock = 0;
|
||||
int rc;
|
||||
struct cifsTconInfo *ptcon = cifs_sb->tcon;
|
||||
struct cifsTconInfo *ptcon = cifs_sb_tcon(cifs_sb);
|
||||
char *tmpbuffer;
|
||||
|
||||
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,
|
|||
static int initiate_cifs_search(const int xid, struct file *file)
|
||||
{
|
||||
int rc = 0;
|
||||
char *full_path;
|
||||
char *full_path = NULL;
|
||||
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;
|
||||
|
||||
if (file->private_data == NULL) {
|
||||
file->private_data =
|
||||
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
||||
}
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink))
|
||||
return PTR_ERR(tlink);
|
||||
pTcon = tlink_tcon(tlink);
|
||||
|
||||
if (file->private_data == NULL)
|
||||
return -ENOMEM;
|
||||
file->private_data =
|
||||
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
||||
if (file->private_data == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
cifsFile = file->private_data;
|
||||
cifsFile->invalidHandle = true;
|
||||
cifsFile->srch_inf.endOfSearch = false;
|
||||
|
||||
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;
|
||||
cifsFile->tlink = cifs_get_tlink(tlink);
|
||||
|
||||
full_path = build_path_from_dentry(file->f_path.dentry);
|
||||
|
||||
if (full_path == NULL)
|
||||
return -ENOMEM;
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto error_exit;
|
||||
}
|
||||
|
||||
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)
|
|||
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
|
||||
goto ffirst_retry;
|
||||
}
|
||||
error_exit:
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -525,14 +528,14 @@ static int find_cifs_entry(const int xid, struct cifsTconInfo *pTcon,
|
|||
(index_to_find < first_entry_in_buffer)) {
|
||||
/* 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 &&
|
||||
!cifsFile->invalidHandle) {
|
||||
cifsFile->invalidHandle = true;
|
||||
write_unlock(&GlobalSMBSeslock);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
CIFSFindClose(xid, pTcon, cifsFile->netfid);
|
||||
} else
|
||||
write_unlock(&GlobalSMBSeslock);
|
||||
spin_unlock(&cifs_file_list_lock);
|
||||
if (cifsFile->srch_inf.ntwrk_buf_start) {
|
||||
cFYI(1, "freeing SMB ff cache buf on search rewind");
|
||||
if (cifsFile->srch_inf.smallBuf)
|
||||
|
@ -738,6 +741,15 @@ static int cifs_filldir(char *pfindEntry, struct file *file, filldir_t filldir,
|
|||
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);
|
||||
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)
|
|||
xid = GetXid();
|
||||
|
||||
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) {
|
||||
case 0:
|
||||
|
@ -804,14 +824,6 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
|||
if it before then restart search
|
||||
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) {
|
||||
rc = -EINVAL;
|
||||
FreeXid(xid);
|
||||
|
@ -829,6 +841,7 @@ int cifs_readdir(struct file *file, void *direntry, filldir_t filldir)
|
|||
CIFSFindClose(xid, pTcon, cifsFile->netfid);
|
||||
} */
|
||||
|
||||
pTcon = tlink_tcon(cifsFile->tlink);
|
||||
rc = find_cifs_entry(xid, pTcon, file,
|
||||
¤t_entry, &num_to_fill);
|
||||
if (rc) {
|
||||
|
|
167
fs/cifs/sess.c
167
fs/cifs/sess.c
|
@ -80,7 +80,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses)
|
|||
if (max_vcs < 2)
|
||||
max_vcs = 0xFFFF;
|
||||
|
||||
write_lock(&cifs_tcp_ses_lock);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
if ((ses->need_reconnect) && is_first_ses_reconnect(ses))
|
||||
goto get_vc_num_exit; /* vcnum will be zero */
|
||||
for (i = ses->server->srv_count - 1; i < max_vcs; i++) {
|
||||
|
@ -112,7 +112,7 @@ static __le16 get_next_vcnum(struct cifsSesInfo *ses)
|
|||
vcnum = i;
|
||||
ses->vcnum = vcnum;
|
||||
get_vc_num_exit:
|
||||
write_unlock(&cifs_tcp_ses_lock);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
return cpu_to_le16(vcnum);
|
||||
}
|
||||
|
@ -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,
|
||||
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;
|
||||
|
||||
if (blob_len < sizeof(CHALLENGE_MESSAGE)) {
|
||||
|
@ -399,12 +402,25 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
|||
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 */
|
||||
/* In particular we can examine sign flags */
|
||||
/* BB spec says that if AvId field of MsvAvTimestamp is populated then
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -425,7 +441,7 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
|||
/* BB is NTLMV2 session security format easier to use here? */
|
||||
flags = NTLMSSP_NEGOTIATE_56 | NTLMSSP_REQUEST_TARGET |
|
||||
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM;
|
||||
NTLMSSP_NEGOTIATE_NTLM;
|
||||
if (ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
||||
flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
|
@ -448,13 +464,16 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
|||
maximum possible size is fixed and small, making this approach cleaner.
|
||||
This function returns the length of the data in the blob */
|
||||
static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
||||
u16 *buflen,
|
||||
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;
|
||||
__u32 flags;
|
||||
unsigned char *tmp;
|
||||
char ntlm_session_key[CIFS_SESS_KEY_SIZE];
|
||||
struct ntlmv2_resp ntlmv2_response = {};
|
||||
|
||||
memcpy(sec_blob->Signature, NTLMSSP_SIGNATURE, 8);
|
||||
sec_blob->MessageType = NtLmAuthenticate;
|
||||
|
@ -462,7 +481,7 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
|||
flags = NTLMSSP_NEGOTIATE_56 |
|
||||
NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_TARGET_INFO |
|
||||
NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
|
||||
NTLMSSP_NEGOTIATE_NT_ONLY | NTLMSSP_NEGOTIATE_NTLM;
|
||||
NTLMSSP_NEGOTIATE_NTLM;
|
||||
if (ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
||||
flags |= NTLMSSP_NEGOTIATE_SIGN;
|
||||
|
@ -477,19 +496,26 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
|||
sec_blob->LmChallengeResponse.Length = 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.Length = cpu_to_le16(CIFS_SESS_KEY_SIZE);
|
||||
sec_blob->NtChallengeResponse.MaximumLength =
|
||||
cpu_to_le16(CIFS_SESS_KEY_SIZE);
|
||||
rc = setup_ntlmv2_rsp(ses, (char *)&ntlmv2_response, nls_cp);
|
||||
if (rc) {
|
||||
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) {
|
||||
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
|
||||
|
@ -501,7 +527,6 @@ static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
|||
len = cifs_strtoUCS((__le16 *)tmp, ses->domainName,
|
||||
MAX_USERNAME_SIZE, nls_cp);
|
||||
len *= 2; /* unicode is 2 bytes each */
|
||||
len += 2; /* trailing null */
|
||||
sec_blob->DomainName.BufferOffset = cpu_to_le32(tmp - pbuffer);
|
||||
sec_blob->DomainName.Length = 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,
|
|||
len = cifs_strtoUCS((__le16 *)tmp, ses->userName,
|
||||
MAX_USERNAME_SIZE, nls_cp);
|
||||
len *= 2; /* unicode is 2 bytes each */
|
||||
len += 2; /* trailing null */
|
||||
sec_blob->UserName.BufferOffset = cpu_to_le32(tmp - pbuffer);
|
||||
sec_blob->UserName.Length = 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,
|
|||
sec_blob->SessionKey.BufferOffset = cpu_to_le32(tmp - pbuffer);
|
||||
sec_blob->SessionKey.Length = 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,
|
|||
|
||||
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
|
||||
|
||||
int
|
||||
|
@ -579,15 +593,12 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
int bytes_remaining;
|
||||
struct key *spnego_key = NULL;
|
||||
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
|
||||
bool first_time;
|
||||
u16 blob_len;
|
||||
char *ntlmsspblob = NULL;
|
||||
|
||||
if (ses == NULL)
|
||||
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;
|
||||
|
||||
cFYI(1, "sess setup type %d", type);
|
||||
|
@ -658,7 +669,7 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
/* BB calculate hash with password */
|
||||
/* 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 ?
|
||||
true : false, lnm_session_key);
|
||||
|
||||
|
@ -685,15 +696,11 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
cpu_to_le16(CIFS_SESS_KEY_SIZE);
|
||||
|
||||
/* calculate session key */
|
||||
SMBNTencrypt(ses->password, ses->server->cryptKey,
|
||||
ntlm_session_key);
|
||||
SMBNTencrypt(ses->password, ses->cryptKey, ntlm_session_key);
|
||||
|
||||
if (first_time) /* should this be moved into common code
|
||||
with similar ntlmv2 path? */
|
||||
cifs_calculate_mac_key(&ses->server->mac_signing_key,
|
||||
ntlm_session_key, ses->password);
|
||||
cifs_calculate_session_key(&ses->auth_key,
|
||||
ntlm_session_key, ses->password);
|
||||
/* copy session key */
|
||||
|
||||
memcpy(bcc_ptr, (char *)ntlm_session_key, CIFS_SESS_KEY_SIZE);
|
||||
bcc_ptr += 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,
|
|||
pSMB->req_no_secext.CaseInsensitivePasswordLength = 0;
|
||||
/* cpu_to_le16(LM2_SESS_KEY_SIZE); */
|
||||
|
||||
pSMB->req_no_secext.CaseSensitivePasswordLength =
|
||||
cpu_to_le16(sizeof(struct ntlmv2_resp));
|
||||
|
||||
/* calculate session key */
|
||||
setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
|
||||
/* FIXME: calculate MAC key */
|
||||
rc = setup_ntlmv2_rsp(ses, v2_sess_key, nls_cp);
|
||||
if (rc) {
|
||||
cERROR(1, "Error %d during NTLMv2 authentication", rc);
|
||||
kfree(v2_sess_key);
|
||||
goto ssetup_exit;
|
||||
}
|
||||
memcpy(bcc_ptr, (char *)v2_sess_key,
|
||||
sizeof(struct ntlmv2_resp));
|
||||
sizeof(struct ntlmv2_resp));
|
||||
bcc_ptr += sizeof(struct ntlmv2_resp);
|
||||
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 (iov[0].iov_len % 2) {
|
||||
*bcc_ptr = 0;
|
||||
|
@ -765,17 +787,14 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
}
|
||||
/* bail out if key is too long */
|
||||
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)",
|
||||
msg->sesskey_len);
|
||||
rc = -EOVERFLOW;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
if (first_time) {
|
||||
ses->server->mac_signing_key.len = msg->sesskey_len;
|
||||
memcpy(ses->server->mac_signing_key.data.krb5,
|
||||
msg->data, msg->sesskey_len);
|
||||
}
|
||||
ses->auth_key.len = msg->sesskey_len;
|
||||
memcpy(ses->auth_key.data.krb5, msg->data, msg->sesskey_len);
|
||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||
capabilities |= CAP_EXTENDED_SECURITY;
|
||||
pSMB->req.Capabilities = cpu_to_le32(capabilities);
|
||||
|
@ -815,12 +834,30 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
if (phase == NtLmNegotiate) {
|
||||
setup_ntlmssp_neg_req(pSMB, ses);
|
||||
iov[1].iov_len = sizeof(NEGOTIATE_MESSAGE);
|
||||
iov[1].iov_base = &pSMB->req.SecurityBlob[0];
|
||||
} else if (phase == NtLmAuthenticate) {
|
||||
int blob_len;
|
||||
blob_len = setup_ntlmssp_auth_req(pSMB, ses,
|
||||
nls_cp,
|
||||
first_time);
|
||||
/* 5 is an empirical value, large enought to
|
||||
* hold authenticate message, max 10 of
|
||||
* av paris, doamin,user,workstation mames,
|
||||
* 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_base = ntlmsspblob;
|
||||
pSMB->req.SecurityBlobLength =
|
||||
cpu_to_le16(blob_len);
|
||||
/* Make sure that we tell the server that we
|
||||
are using the uid that it just gave us back
|
||||
on the response (challenge) */
|
||||
|
@ -830,7 +867,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
rc = -ENOSYS;
|
||||
goto ssetup_exit;
|
||||
}
|
||||
iov[1].iov_base = &pSMB->req.SecurityBlob[0];
|
||||
/* unicode strings must be word aligned */
|
||||
if ((iov[0].iov_len + iov[1].iov_len) % 2) {
|
||||
*bcc_ptr = 0;
|
||||
|
@ -895,7 +931,6 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
bcc_ptr = pByteArea(smb_buf);
|
||||
|
||||
if (smb_buf->WordCount == 4) {
|
||||
__u16 blob_len;
|
||||
blob_len = le16_to_cpu(pSMB->resp.SecurityBlobLength);
|
||||
if (blob_len > bytes_remaining) {
|
||||
cERROR(1, "bad security blob length %d", blob_len);
|
||||
|
@ -931,6 +966,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
key_put(spnego_key);
|
||||
}
|
||||
kfree(str_area);
|
||||
kfree(ntlmsspblob);
|
||||
ntlmsspblob = NULL;
|
||||
if (resp_buf_type == CIFS_SMALL_BUFFER) {
|
||||
cFYI(1, "ssetup freeing small buf %p", iov[0].iov_base);
|
||||
cifs_small_buf_release(iov[0].iov_base);
|
||||
|
|
|
@ -543,7 +543,7 @@ SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
|
|||
(ses->server->secMode & (SECMODE_SIGN_REQUIRED |
|
||||
SECMODE_SIGN_ENABLED))) {
|
||||
rc = cifs_verify_signature(midQ->resp_buf,
|
||||
&ses->server->mac_signing_key,
|
||||
&ses->server->session_key,
|
||||
midQ->sequence_number+1);
|
||||
if (rc) {
|
||||
cERROR(1, "Unexpected SMB signature");
|
||||
|
@ -731,7 +731,7 @@ SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
|
|||
(ses->server->secMode & (SECMODE_SIGN_REQUIRED |
|
||||
SECMODE_SIGN_ENABLED))) {
|
||||
rc = cifs_verify_signature(out_buf,
|
||||
&ses->server->mac_signing_key,
|
||||
&ses->server->session_key,
|
||||
midQ->sequence_number+1);
|
||||
if (rc) {
|
||||
cERROR(1, "Unexpected SMB signature");
|
||||
|
@ -981,7 +981,7 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon,
|
|||
(ses->server->secMode & (SECMODE_SIGN_REQUIRED |
|
||||
SECMODE_SIGN_ENABLED))) {
|
||||
rc = cifs_verify_signature(out_buf,
|
||||
&ses->server->mac_signing_key,
|
||||
&ses->server->session_key,
|
||||
midQ->sequence_number+1);
|
||||
if (rc) {
|
||||
cERROR(1, "Unexpected SMB signature");
|
||||
|
|
|
@ -47,9 +47,10 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
|
|||
#ifdef CONFIG_CIFS_XATTR
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct super_block *sb;
|
||||
char *full_path;
|
||||
char *full_path = NULL;
|
||||
|
||||
if (direntry == NULL)
|
||||
return -EIO;
|
||||
|
@ -58,16 +59,19 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
|
|||
sb = direntry->d_inode->i_sb;
|
||||
if (sb == NULL)
|
||||
return -EIO;
|
||||
xid = GetXid();
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto remove_ea_exit;
|
||||
}
|
||||
if (ea_name == NULL) {
|
||||
cFYI(1, "Null xattr names not supported");
|
||||
|
@ -91,6 +95,7 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)
|
|||
remove_ea_exit:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
@ -102,6 +107,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
|
|||
#ifdef CONFIG_CIFS_XATTR
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct super_block *sb;
|
||||
char *full_path;
|
||||
|
@ -113,16 +119,19 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
|
|||
sb = direntry->d_inode->i_sb;
|
||||
if (sb == NULL)
|
||||
return -EIO;
|
||||
xid = GetXid();
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto set_ea_exit;
|
||||
}
|
||||
/* return dos attributes as pseudo xattr */
|
||||
/* return alt name if available as pseudo attr */
|
||||
|
@ -132,9 +141,8 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
|
|||
returns as xattrs */
|
||||
if (value_size > MAX_EA_VALUE_SIZE) {
|
||||
cFYI(1, "size of EA value too large");
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
return -EOPNOTSUPP;
|
||||
rc = -EOPNOTSUPP;
|
||||
goto set_ea_exit;
|
||||
}
|
||||
|
||||
if (ea_name == NULL) {
|
||||
|
@ -198,6 +206,7 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,
|
|||
set_ea_exit:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
@ -209,6 +218,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
|
|||
#ifdef CONFIG_CIFS_XATTR
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct super_block *sb;
|
||||
char *full_path;
|
||||
|
@ -221,16 +231,18 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
|
|||
if (sb == NULL)
|
||||
return -EIO;
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto get_ea_exit;
|
||||
}
|
||||
/* return dos attributes as pseudo xattr */
|
||||
/* return alt name if available as pseudo attr */
|
||||
|
@ -323,6 +335,7 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,
|
|||
get_ea_exit:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
@ -333,6 +346,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
|
|||
#ifdef CONFIG_CIFS_XATTR
|
||||
int xid;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct super_block *sb;
|
||||
char *full_path;
|
||||
|
@ -346,18 +360,20 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
|
|||
return -EIO;
|
||||
|
||||
cifs_sb = CIFS_SB(sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
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);
|
||||
if (full_path == NULL) {
|
||||
rc = -ENOMEM;
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
goto list_ea_exit;
|
||||
}
|
||||
/* return dos attributes as pseudo xattr */
|
||||
/* 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)
|
|||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
|
||||
list_ea_exit:
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue