提交 d2ecad9f 编写于 作者: L Linus Torvalds

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

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

Fix up trivial conflict in fs/cifs/cifsfs.c due to added/removed header files
......@@ -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;
......
......@@ -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,7 +617,7 @@ 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 &&
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
......@@ -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;
......@@ -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
......@@ -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,12 +620,14 @@ 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
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
......
/*
* 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 */
此差异已折叠。
......@@ -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,21 +172,26 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
xid = GetXid();
cifs_sb = CIFS_SB(inode->i_sb);
tcon = cifs_sb->tcon;
full_path = build_path_from_dentry(direntry);
if (full_path == NULL) {
rc = -ENOMEM;
goto cifs_create_out;
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.flags;
oflags = nd->intent.open.file->f_flags;
else
oflags = FMODE_READ | SMB_O_CREAT;
oflags = O_RDONLY | O_CREAT;
full_path = build_path_from_dentry(direntry);
if (full_path == NULL) {
rc = -ENOMEM;
goto cifs_create_out;
}
if (tcon->unix_ext && (tcon->ses->capabilities & CAP_UNIX) &&
(CIFS_UNIX_POSIX_PATH_OPS_CAP &
......@@ -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);
}
......
此差异已折叠。
......@@ -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",
......
此差异已折叠。
......@@ -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");
......
......@@ -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,17 +327,17 @@ 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;
xid = GetXid();
cifs_sb_target = CIFS_SB(inode->i_sb);
pTcon = cifs_sb_target->tcon;
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
/* 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) */
xid = GetXid();
fromName = build_path_from_dentry(old_file);
toName = build_path_from_dentry(direntry);
......@@ -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 = -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) {
tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink))
return PTR_ERR(tlink);
pTcon = tlink_tcon(tlink);
if (file->private_data == NULL)
file->private_data =
kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
if (file->private_data == NULL) {
rc = -ENOMEM;
goto error_exit;
}
if (file->private_data == NULL)
return -ENOMEM;
cifsFile = file->private_data;
cifsFile->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,
&current_entry, &num_to_fill);
if (rc) {
......
此差异已折叠。
......@@ -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");
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册