Merge branch 'for-next'
This commit is contained in:
commit
6ea75952d7
|
@ -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.
|
||||
|
|
|
@ -306,6 +306,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 +316,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 +328,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,10 +38,13 @@
|
|||
#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;
|
||||
|
@ -47,12 +52,13 @@ struct cifs_sb_info {
|
|||
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,90 @@ void calc_lanman_hash(const char *password, const char *cryptkey, bool encrypt,
|
|||
}
|
||||
#endif /* CIFS_WEAK_PW_HASH */
|
||||
|
||||
/* This is just a filler for ntlmv2 type of security mechanisms.
|
||||
* Older servers are not very particular about the contents of av pairs
|
||||
* in the blob and for sec mechs like ntlmv2, there is no negotiation
|
||||
* as in ntlmssp, so unless domain and server netbios and dns names
|
||||
* are specified, there is no way to obtain name. In case of ntlmssp,
|
||||
* server provides that info in type 2 challenge packet
|
||||
*/
|
||||
static int
|
||||
build_avpair_blob(struct cifsSesInfo *ses)
|
||||
{
|
||||
struct ntlmssp2_name *attrptr;
|
||||
|
||||
ses->tilen = 2 * sizeof(struct ntlmssp2_name);
|
||||
ses->tiblob = kzalloc(ses->tilen, GFP_KERNEL);
|
||||
if (!ses->tiblob) {
|
||||
ses->tilen = 0;
|
||||
cERROR(1, "Challenge target info allocation failure");
|
||||
return -ENOMEM;
|
||||
}
|
||||
attrptr = (struct ntlmssp2_name *) ses->tiblob;
|
||||
attrptr->type = cpu_to_le16(NTLMSSP_DOMAIN_TYPE);
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -321,7 +406,8 @@ calc_exit_2:
|
|||
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 +419,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);
|
||||
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);
|
||||
hmac_md5_update(resp_buf, 16, &context);
|
||||
hmac_md5_final(ses->server->mac_signing_key.data.ntlmv2.key, &context);
|
||||
hmac_md5_final(ses->server->session_key.data.ntlmv2.key, &context);
|
||||
|
||||
memcpy(&ses->server->mac_signing_key.data.ntlmv2.resp, resp_buf,
|
||||
memcpy(&ses->server->session_key.data.ntlmv2.resp, resp_buf,
|
||||
sizeof(struct ntlmv2_resp));
|
||||
ses->server->mac_signing_key.len = 16 + sizeof(struct ntlmv2_resp);
|
||||
ses->server->session_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,
|
||||
|
@ -365,6 +474,9 @@ void CalcNTLMv2_response(const struct cifsSesInfo *ses,
|
|||
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); */
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include <linux/kthread.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <net/ipv6.h>
|
||||
#include "cifsfs.h"
|
||||
#include "cifspdu.h"
|
||||
#define DECLARE_GLOBALS_HERE
|
||||
|
@ -136,9 +137,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);
|
||||
|
@ -224,7 +222,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;
|
||||
|
||||
|
@ -366,14 +364,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");
|
||||
|
@ -422,6 +442,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);
|
||||
|
@ -437,9 +459,7 @@ 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);
|
||||
if ((tcon->tc_count > 1) || (tcon->tidStatus == CifsExiting)) {
|
||||
|
@ -568,6 +588,7 @@ static int cifs_setlease(struct file *file, long arg, struct file_lock **lease)
|
|||
/* note that this is called by vfs setlease with the BKL held
|
||||
although I doubt that BKL is needed here in cifs */
|
||||
struct inode *inode = file->f_path.dentry->d_inode;
|
||||
struct cifsFileInfo *cfile = file->private_data;
|
||||
|
||||
if (!(S_ISREG(inode->i_mode)))
|
||||
return -EINVAL;
|
||||
|
@ -578,7 +599,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
|
||||
|
@ -912,11 +933,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)
|
||||
|
@ -938,19 +959,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -182,7 +183,7 @@ struct TCP_Server_Info {
|
|||
/* 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;
|
||||
struct session_key session_key;
|
||||
char ntlmv2_hash[16];
|
||||
unsigned long lstrp; /* when we got last response from this server */
|
||||
u16 dialect; /* dialect index that server chose */
|
||||
|
@ -222,6 +223,8 @@ struct cifsSesInfo {
|
|||
char userName[MAX_USERNAME_SIZE + 1];
|
||||
char *domainName;
|
||||
char *password;
|
||||
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 +310,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
|
||||
|
@ -348,6 +389,7 @@ struct cifsFileInfo {
|
|||
struct file *pfile; /* needed for writepage */
|
||||
struct inode *pInode; /* needed for oplock break */
|
||||
struct vfsmount *mnt;
|
||||
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 */
|
||||
|
@ -369,6 +411,7 @@ static inline void cifsFileInfo_get(struct cifsFileInfo *cifs_file)
|
|||
static inline void cifsFileInfo_put(struct cifsFileInfo *cifs_file)
|
||||
{
|
||||
if (atomic_dec_and_test(&cifs_file->count)) {
|
||||
cifs_put_tlink(cifs_file->tlink);
|
||||
iput(cifs_file->pInode);
|
||||
kfree(cifs_file);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
@ -107,7 +107,8 @@ extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,
|
|||
|
||||
extern struct cifsFileInfo *cifs_new_fileinfo(struct inode *newinode,
|
||||
__u16 fileHandle, struct file *file,
|
||||
struct vfsmount *mnt, unsigned int oflags);
|
||||
struct vfsmount *mnt, struct tcon_link *tlink,
|
||||
unsigned int oflags, __u32 oplock);
|
||||
extern int cifs_posix_open(char *full_path, struct inode **pinode,
|
||||
struct super_block *sb,
|
||||
int mode, int oflags,
|
||||
|
@ -362,12 +363,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 +409,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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -1046,6 +1053,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 +1348,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 +1383,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 +1408,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 +1464,9 @@ match_address(struct TCP_Server_Info *server, struct sockaddr *addr)
|
|||
break;
|
||||
}
|
||||
|
||||
if (!srcip_matches(srcaddr, (struct sockaddr *)&server->srcaddr))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1460,16 +1525,8 @@ cifs_find_tcp_session(struct sockaddr *addr, struct smb_vol *vol)
|
|||
|
||||
write_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))
|
||||
|
@ -1584,6 +1641,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) {
|
||||
|
@ -1740,6 +1799,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)
|
||||
|
@ -1913,6 +1974,23 @@ out_fail:
|
|||
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 +2075,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 +2127,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 +2293,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 +2496,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 +2577,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 +2678,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 +2690,7 @@ try_mount_again:
|
|||
pSesInfo = NULL;
|
||||
srvTcp = NULL;
|
||||
full_path = NULL;
|
||||
tlink = NULL;
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
|
@ -2638,8 +2766,6 @@ try_mount_again:
|
|||
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 +2874,38 @@ remote_path_check:
|
|||
#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) {
|
||||
|
@ -2934,19 +3092,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)
|
||||
|
@ -3007,3 +3185,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 */
|
||||
write_lock(&cifs_tcp_ses_lock);
|
||||
++master_tcon->ses->server->srv_count;
|
||||
write_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);
|
||||
}
|
||||
|
|
103
fs/cifs/dir.c
103
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 @@ cifs_bp_rename_retry:
|
|||
/* 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++) {
|
||||
|
@ -131,28 +131,26 @@ cifs_bp_rename_retry:
|
|||
}
|
||||
|
||||
struct cifsFileInfo *
|
||||
cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
|
||||
struct file *file, struct vfsmount *mnt, unsigned int oflags)
|
||||
cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle, struct file *file,
|
||||
struct vfsmount *mnt, struct tcon_link *tlink,
|
||||
unsigned int oflags, __u32 oplock)
|
||||
{
|
||||
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->uid = current_fsuid();
|
||||
pCifsFile->pInode = igrab(newinode);
|
||||
pCifsFile->mnt = mnt;
|
||||
pCifsFile->pfile = file;
|
||||
pCifsFile->invalidHandle = false;
|
||||
pCifsFile->closePend = false;
|
||||
pCifsFile->tlink = cifs_get_tlink(tlink);
|
||||
mutex_init(&pCifsFile->fh_mutex);
|
||||
mutex_init(&pCifsFile->lock_mutex);
|
||||
INIT_LIST_HEAD(&pCifsFile->llist);
|
||||
|
@ -160,7 +158,7 @@ cifs_new_fileinfo(struct inode *newinode, __u16 fileHandle,
|
|||
INIT_WORK(&pCifsFile->oplock_break, cifs_oplock_break);
|
||||
|
||||
write_lock(&GlobalSMBSeslock);
|
||||
list_add(&pCifsFile->tlist, &cifs_sb->tcon->openFileList);
|
||||
list_add(&pCifsFile->tlist, &(tlink_tcon(tlink)->openFileList));
|
||||
pCifsInode = CIFS_I(newinode);
|
||||
if (pCifsInode) {
|
||||
/* if readable file instance put first in list*/
|
||||
|
@ -193,6 +191,8 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
|
|||
__u32 posix_flags = 0;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||
struct cifs_fattr fattr;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsTconInfo *tcon;
|
||||
|
||||
cFYI(1, "posix open %s", full_path);
|
||||
|
||||
|
@ -227,10 +227,20 @@ int cifs_posix_open(char *full_path, struct inode **pinode,
|
|||
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 &
|
||||
|
||||
tlink = cifs_sb_tlink(cifs_sb);
|
||||
if (IS_ERR(tlink)) {
|
||||
rc = PTR_ERR(tlink);
|
||||
goto posix_open_ret;
|
||||
}
|
||||
|
||||
tcon = tlink_tcon(tlink);
|
||||
rc = CIFSPOSIXCreate(xid, tcon, posix_flags, mode, pnetfid, presp_data,
|
||||
poplock, full_path, cifs_sb->local_nls,
|
||||
cifs_sb->mnt_cifs_flags &
|
||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||
cifs_put_tlink(tlink);
|
||||
|
||||
if (rc)
|
||||
goto posix_open_ret;
|
||||
|
||||
|
@ -291,6 +301,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,13 +311,12 @@ 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;
|
||||
|
@ -316,6 +326,12 @@ cifs_create(struct inode *inode, struct dentry *direntry, int mode,
|
|||
else
|
||||
oflags = FMODE_READ | SMB_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 &
|
||||
le64_to_cpu(tcon->fsUnixInfo.Capability))) {
|
||||
|
@ -375,7 +391,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,
|
||||
|
@ -468,7 +484,8 @@ cifs_create_set_dentry:
|
|||
}
|
||||
|
||||
pfile_info = cifs_new_fileinfo(newinode, fileHandle, filp,
|
||||
nd->path.mnt, oflags);
|
||||
nd->path.mnt, tlink, oflags,
|
||||
oplock);
|
||||
if (pfile_info == NULL) {
|
||||
fput(filp);
|
||||
CIFSSMBClose(xid, tcon, fileHandle);
|
||||
|
@ -481,6 +498,7 @@ cifs_create_set_dentry:
|
|||
cifs_create_out:
|
||||
kfree(buf);
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
FreeXid(xid);
|
||||
return rc;
|
||||
}
|
||||
|
@ -491,6 +509,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 +522,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 +629,7 @@ mknod_out:
|
|||
kfree(full_path);
|
||||
kfree(buf);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -619,6 +643,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 +658,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 +674,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 +685,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 +694,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) {
|
||||
|
@ -728,8 +759,9 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,
|
|||
}
|
||||
|
||||
cfile = cifs_new_fileinfo(newInode, fileHandle, filp,
|
||||
nd->path.mnt,
|
||||
nd->intent.open.flags);
|
||||
nd->path.mnt, tlink,
|
||||
nd->intent.open.flags,
|
||||
oplock);
|
||||
if (cfile == NULL) {
|
||||
fput(filp);
|
||||
CIFSSMBClose(xid, pTcon, fileHandle);
|
||||
|
@ -759,6 +791,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);
|
||||
}
|
||||
|
|
138
fs/cifs/file.c
138
fs/cifs/file.c
|
@ -165,7 +165,7 @@ psx_client_can_cache:
|
|||
|
||||
/* all arguments to this function must be checked for validity in caller */
|
||||
static inline int cifs_open_inode_helper(struct inode *inode,
|
||||
struct cifsTconInfo *pTcon, int *oplock, FILE_ALL_INFO *buf,
|
||||
struct cifsTconInfo *pTcon, __u32 oplock, FILE_ALL_INFO *buf,
|
||||
char *full_path, int xid)
|
||||
{
|
||||
struct cifsInodeInfo *pCifsInode = CIFS_I(inode);
|
||||
|
@ -207,11 +207,11 @@ client_can_cache:
|
|||
rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb,
|
||||
xid, NULL);
|
||||
|
||||
if ((*oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
||||
if ((oplock & 0xF) == OPLOCK_EXCLUSIVE) {
|
||||
pCifsInode->clientCanCacheAll = true;
|
||||
pCifsInode->clientCanCacheRead = true;
|
||||
cFYI(1, "Exclusive Oplock granted on inode %p", inode);
|
||||
} else if ((*oplock & 0xF) == OPLOCK_READ)
|
||||
} else if ((oplock & 0xF) == OPLOCK_READ)
|
||||
pCifsInode->clientCanCacheRead = true;
|
||||
|
||||
return rc;
|
||||
|
@ -224,6 +224,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
__u32 oplock;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifsTconInfo *tcon;
|
||||
struct tcon_link *tlink;
|
||||
struct cifsFileInfo *pCifsFile = NULL;
|
||||
struct cifsInodeInfo *pCifsInode;
|
||||
char *full_path = NULL;
|
||||
|
@ -235,7 +236,12 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
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);
|
||||
|
||||
pCifsInode = CIFS_I(file->f_path.dentry->d_inode);
|
||||
|
||||
|
@ -277,7 +283,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
|
||||
pCifsFile = cifs_new_fileinfo(inode, netfid, file,
|
||||
file->f_path.mnt,
|
||||
oflags);
|
||||
tlink, oflags, oplock);
|
||||
if (pCifsFile == NULL) {
|
||||
CIFSSMBClose(xid, tcon, netfid);
|
||||
rc = -ENOMEM;
|
||||
|
@ -345,7 +351,7 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (cifs_sb->tcon->ses->capabilities & CAP_NT_SMBS)
|
||||
if (tcon->ses->capabilities & CAP_NT_SMBS)
|
||||
rc = CIFSSMBOpen(xid, tcon, full_path, disposition,
|
||||
desiredAccess, CREATE_NOT_DIR, &netfid, &oplock, buf,
|
||||
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags
|
||||
|
@ -365,12 +371,12 @@ int cifs_open(struct inode *inode, struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
rc = cifs_open_inode_helper(inode, tcon, &oplock, buf, full_path, xid);
|
||||
rc = cifs_open_inode_helper(inode, tcon, oplock, buf, full_path, xid);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
pCifsFile = cifs_new_fileinfo(inode, netfid, file, file->f_path.mnt,
|
||||
file->f_flags);
|
||||
tlink, file->f_flags, oplock);
|
||||
if (pCifsFile == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
|
@ -402,6 +408,7 @@ out:
|
|||
kfree(buf);
|
||||
kfree(full_path);
|
||||
FreeXid(xid);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -461,7 +468,7 @@ static int cifs_reopen_file(struct file *file, bool can_flush)
|
|||
}
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
tcon = cifs_sb->tcon;
|
||||
tcon = tlink_tcon(pCifsFile->tlink);
|
||||
|
||||
/* can not grab rename sem here because various ops, including
|
||||
those that already have the rename sem can end up causing writepage
|
||||
|
@ -575,7 +582,7 @@ int cifs_close(struct inode *inode, struct file *file)
|
|||
xid = GetXid();
|
||||
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
pTcon = tlink_tcon(pSMBFile->tlink);
|
||||
if (pSMBFile) {
|
||||
struct cifsLockInfo *li, *tmp;
|
||||
write_lock(&GlobalSMBSeslock);
|
||||
|
@ -653,11 +660,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|||
xid = GetXid();
|
||||
|
||||
if (pCFileStruct) {
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct cifs_sb_info *cifs_sb =
|
||||
CIFS_SB(file->f_path.dentry->d_sb);
|
||||
|
||||
pTcon = cifs_sb->tcon;
|
||||
struct cifsTconInfo *pTcon = tlink_tcon(pCFileStruct->tlink);
|
||||
|
||||
cFYI(1, "Freeing private data in close dir");
|
||||
write_lock(&GlobalSMBSeslock);
|
||||
|
@ -681,6 +684,7 @@ int cifs_closedir(struct inode *inode, struct file *file)
|
|||
else
|
||||
cifs_buf_release(ptmp);
|
||||
}
|
||||
cifs_put_tlink(pCFileStruct->tlink);
|
||||
kfree(file->private_data);
|
||||
file->private_data = NULL;
|
||||
}
|
||||
|
@ -767,7 +771,7 @@ int cifs_lock(struct file *file, int cmd, struct file_lock *pfLock)
|
|||
cFYI(1, "Unknown type of lock");
|
||||
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
tcon = cifs_sb->tcon;
|
||||
tcon = tlink_tcon(((struct cifsFileInfo *)file->private_data)->tlink);
|
||||
|
||||
if (file->private_data == NULL) {
|
||||
rc = -EBADF;
|
||||
|
@ -960,14 +964,14 @@ ssize_t cifs_user_write(struct file *file, const char __user *write_data,
|
|||
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
/* cFYI(1, " write %d bytes to offset %lld of %s", write_size,
|
||||
*poffset, file->f_path.dentry->d_name.name); */
|
||||
|
||||
if (file->private_data == NULL)
|
||||
return -EBADF;
|
||||
|
||||
open_file = file->private_data;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
|
||||
rc = generic_write_checks(file, poffset, &write_size, 0);
|
||||
if (rc)
|
||||
|
@ -1062,14 +1066,13 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
|||
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
cFYI(1, "write %zd bytes to offset %lld of %s", write_size,
|
||||
*poffset, file->f_path.dentry->d_name.name);
|
||||
|
||||
if (file->private_data == NULL)
|
||||
return -EBADF;
|
||||
open_file = file->private_data;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
|
@ -1165,9 +1168,15 @@ static ssize_t cifs_write(struct file *file, const char *write_data,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||
struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
||||
struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
|
||||
bool fsuid_only)
|
||||
{
|
||||
struct cifsFileInfo *open_file = NULL;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
||||
|
||||
/* only filter by fsuid on multiuser mounts */
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
||||
fsuid_only = false;
|
||||
|
||||
read_lock(&GlobalSMBSeslock);
|
||||
/* we could simply get the first_list_entry since write-only entries
|
||||
|
@ -1176,6 +1185,8 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
|||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||
if (open_file->closePend)
|
||||
continue;
|
||||
if (fsuid_only && open_file->uid != current_fsuid())
|
||||
continue;
|
||||
if (open_file->pfile && ((open_file->pfile->f_flags & O_RDWR) ||
|
||||
(open_file->pfile->f_flags & O_RDONLY))) {
|
||||
if (!open_file->invalidHandle) {
|
||||
|
@ -1195,9 +1206,11 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode)
|
|||
}
|
||||
#endif
|
||||
|
||||
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
||||
struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode,
|
||||
bool fsuid_only)
|
||||
{
|
||||
struct cifsFileInfo *open_file;
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(cifs_inode->vfs_inode.i_sb);
|
||||
bool any_available = false;
|
||||
int rc;
|
||||
|
||||
|
@ -1211,13 +1224,19 @@ struct cifsFileInfo *find_writable_file(struct cifsInodeInfo *cifs_inode)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/* only filter by fsuid on multiuser mounts */
|
||||
if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
|
||||
fsuid_only = false;
|
||||
|
||||
read_lock(&GlobalSMBSeslock);
|
||||
refind_writable:
|
||||
list_for_each_entry(open_file, &cifs_inode->openFileList, flist) {
|
||||
if (open_file->closePend ||
|
||||
(!any_available && open_file->pid != current->tgid))
|
||||
if (open_file->closePend)
|
||||
continue;
|
||||
if (!any_available && open_file->pid != current->tgid)
|
||||
continue;
|
||||
if (fsuid_only && open_file->uid != current_fsuid())
|
||||
continue;
|
||||
|
||||
if (open_file->pfile &&
|
||||
((open_file->pfile->f_flags & O_RDWR) ||
|
||||
(open_file->pfile->f_flags & O_WRONLY))) {
|
||||
|
@ -1284,7 +1303,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|||
int rc = -EFAULT;
|
||||
int bytes_written = 0;
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifsTconInfo *pTcon;
|
||||
struct inode *inode;
|
||||
struct cifsFileInfo *open_file;
|
||||
|
||||
|
@ -1293,7 +1311,6 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|||
|
||||
inode = page->mapping->host;
|
||||
cifs_sb = CIFS_SB(inode->i_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
offset += (loff_t)from;
|
||||
write_data = kmap(page);
|
||||
|
@ -1314,7 +1331,7 @@ static int cifs_partialpagewrite(struct page *page, unsigned from, unsigned to)
|
|||
if (mapping->host->i_size - offset < (loff_t)to)
|
||||
to = (unsigned)(mapping->host->i_size - offset);
|
||||
|
||||
open_file = find_writable_file(CIFS_I(mapping->host));
|
||||
open_file = find_writable_file(CIFS_I(mapping->host), false);
|
||||
if (open_file) {
|
||||
bytes_written = cifs_write(open_file->pfile, write_data,
|
||||
to-from, &offset);
|
||||
|
@ -1352,6 +1369,7 @@ static int cifs_writepages(struct address_space *mapping,
|
|||
int nr_pages;
|
||||
__u64 offset = 0;
|
||||
struct cifsFileInfo *open_file;
|
||||
struct cifsTconInfo *tcon;
|
||||
struct cifsInodeInfo *cifsi = CIFS_I(mapping->host);
|
||||
struct page *page;
|
||||
struct pagevec pvec;
|
||||
|
@ -1359,6 +1377,15 @@ static int cifs_writepages(struct address_space *mapping,
|
|||
int scanned = 0;
|
||||
int xid, long_op;
|
||||
|
||||
/*
|
||||
* BB: Is this meaningful for a non-block-device file system?
|
||||
* If it is, we should test it again after we do I/O
|
||||
*/
|
||||
if (wbc->nonblocking && bdi_write_congested(bdi)) {
|
||||
wbc->encountered_congestion = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
cifs_sb = CIFS_SB(mapping->host->i_sb);
|
||||
|
||||
/*
|
||||
|
@ -1368,27 +1395,29 @@ static int cifs_writepages(struct address_space *mapping,
|
|||
if (cifs_sb->wsize < PAGE_CACHE_SIZE)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
if ((cifs_sb->tcon->ses) && (cifs_sb->tcon->ses->server))
|
||||
if (cifs_sb->tcon->ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
|
||||
if (!experimEnabled)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
iov = kmalloc(32 * sizeof(struct kvec), GFP_KERNEL);
|
||||
if (iov == NULL)
|
||||
return generic_writepages(mapping, wbc);
|
||||
|
||||
|
||||
/*
|
||||
* BB: Is this meaningful for a non-block-device file system?
|
||||
* If it is, we should test it again after we do I/O
|
||||
* if there's no open file, then this is likely to fail too,
|
||||
* but it'll at least handle the return. Maybe it should be
|
||||
* a BUG() instead?
|
||||
*/
|
||||
if (wbc->nonblocking && bdi_write_congested(bdi)) {
|
||||
wbc->encountered_congestion = 1;
|
||||
open_file = find_writable_file(CIFS_I(mapping->host), false);
|
||||
if (!open_file) {
|
||||
kfree(iov);
|
||||
return 0;
|
||||
return generic_writepages(mapping, wbc);
|
||||
}
|
||||
|
||||
tcon = tlink_tcon(open_file->tlink);
|
||||
if (!experimEnabled && tcon->ses->server->secMode &
|
||||
(SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED)) {
|
||||
cifsFileInfo_put(open_file);
|
||||
return generic_writepages(mapping, wbc);
|
||||
}
|
||||
cifsFileInfo_put(open_file);
|
||||
|
||||
xid = GetXid();
|
||||
|
||||
pagevec_init(&pvec, 0);
|
||||
|
@ -1492,23 +1521,20 @@ retry:
|
|||
break;
|
||||
}
|
||||
if (n_iov) {
|
||||
/* Search for a writable handle every time we call
|
||||
* CIFSSMBWrite2. We can't rely on the last handle
|
||||
* we used to still be valid
|
||||
*/
|
||||
open_file = find_writable_file(CIFS_I(mapping->host));
|
||||
open_file = find_writable_file(CIFS_I(mapping->host),
|
||||
false);
|
||||
if (!open_file) {
|
||||
cERROR(1, "No writable handles for inode");
|
||||
rc = -EBADF;
|
||||
} else {
|
||||
long_op = cifs_write_timeout(cifsi, offset);
|
||||
rc = CIFSSMBWrite2(xid, cifs_sb->tcon,
|
||||
open_file->netfid,
|
||||
rc = CIFSSMBWrite2(xid, tcon, open_file->netfid,
|
||||
bytes_to_write, offset,
|
||||
&bytes_written, iov, n_iov,
|
||||
long_op);
|
||||
cifsFileInfo_put(open_file);
|
||||
cifs_update_eof(cifsi, offset, bytes_written);
|
||||
}
|
||||
|
||||
if (rc || bytes_written < bytes_to_write) {
|
||||
cERROR(1, "Write2 ret %d, wrote %d",
|
||||
|
@ -1520,10 +1546,9 @@ retry:
|
|||
else
|
||||
set_bit(AS_EIO, &mapping->flags);
|
||||
} else {
|
||||
cifs_stats_bytes_written(cifs_sb->tcon,
|
||||
bytes_written);
|
||||
}
|
||||
cifs_stats_bytes_written(tcon, bytes_written);
|
||||
}
|
||||
|
||||
for (i = 0; i < n_iov; i++) {
|
||||
page = pvec.pages[first + i];
|
||||
/* Should we also set page error on
|
||||
|
@ -1665,7 +1690,7 @@ int cifs_fsync(struct file *file, int datasync)
|
|||
if (rc == 0) {
|
||||
rc = CIFS_I(inode)->write_behind_rc;
|
||||
CIFS_I(inode)->write_behind_rc = 0;
|
||||
tcon = CIFS_SB(inode->i_sb)->tcon;
|
||||
tcon = tlink_tcon(smbfile->tlink);
|
||||
if (!rc && tcon && smbfile &&
|
||||
!(CIFS_SB(inode->i_sb)->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC))
|
||||
rc = CIFSSMBFlush(xid, tcon, smbfile->netfid);
|
||||
|
@ -1750,7 +1775,6 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
|
|||
|
||||
xid = GetXid();
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
if (file->private_data == NULL) {
|
||||
rc = -EBADF;
|
||||
|
@ -1758,6 +1782,7 @@ ssize_t cifs_user_read(struct file *file, char __user *read_data,
|
|||
return rc;
|
||||
}
|
||||
open_file = file->private_data;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
||||
cFYI(1, "attempting read on write only file instance");
|
||||
|
@ -1831,7 +1856,6 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
|
|||
|
||||
xid = GetXid();
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
|
||||
if (file->private_data == NULL) {
|
||||
rc = -EBADF;
|
||||
|
@ -1839,6 +1863,7 @@ static ssize_t cifs_read(struct file *file, char *read_data, size_t read_size,
|
|||
return rc;
|
||||
}
|
||||
open_file = file->private_data;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) == O_WRONLY)
|
||||
cFYI(1, "attempting read on write only file instance");
|
||||
|
@ -1974,7 +1999,7 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,
|
|||
}
|
||||
open_file = file->private_data;
|
||||
cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);
|
||||
pTcon = cifs_sb->tcon;
|
||||
pTcon = tlink_tcon(open_file->tlink);
|
||||
|
||||
/*
|
||||
* Reads as many pages as possible from fscache. Returns -ENOBUFS
|
||||
|
@ -2312,7 +2337,6 @@ void cifs_oplock_break(struct work_struct *work)
|
|||
oplock_break);
|
||||
struct inode *inode = cfile->pInode;
|
||||
struct cifsInodeInfo *cinode = CIFS_I(inode);
|
||||
struct cifs_sb_info *cifs_sb = CIFS_SB(cfile->mnt->mnt_sb);
|
||||
int rc, waitrc = 0;
|
||||
|
||||
if (inode && S_ISREG(inode->i_mode)) {
|
||||
|
@ -2339,8 +2363,8 @@ void cifs_oplock_break(struct work_struct *work)
|
|||
* disconnected since oplock already released by the server
|
||||
*/
|
||||
if (!cfile->closePend && !cfile->oplock_break_cancelled) {
|
||||
rc = CIFSSMBLock(0, cifs_sb->tcon, cfile->netfid, 0, 0, 0, 0,
|
||||
LOCKING_ANDX_OPLOCK_RELEASE, false);
|
||||
rc = CIFSSMBLock(0, tlink_tcon(cfile->tlink), cfile->netfid, 0,
|
||||
0, 0, 0, LOCKING_ANDX_OPLOCK_RELEASE, false);
|
||||
cFYI(1, "Oplock release rc = %d", 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",
|
||||
|
|
229
fs/cifs/inode.c
229
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 @@ retry_iget5_locked:
|
|||
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 @@ set_via_filehandle:
|
|||
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 @@ out_close:
|
|||
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 @@ out_reval:
|
|||
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 @@ mkdir_get_info:
|
|||
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 @@ cifs_rename_exit:
|
|||
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,
|
||||
|
@ -1662,11 +1757,21 @@ check_inval:
|
|||
int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
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 ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) &&
|
||||
!tcon->unix_ext) {
|
||||
if (!cifs_sb->mnt_uid)
|
||||
stat->uid = current_fsuid();
|
||||
if (!cifs_sb->mnt_uid)
|
||||
stat->gid = current_fsgid();
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
@ -1708,7 +1813,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 +1825,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 +1844,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 +1880,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 +1902,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 +1990,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 +2189,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");
|
||||
|
|
368
fs/cifs/link.c
368
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_hl_exit:
|
|||
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 @@ out:
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -729,6 +729,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 @@ ffirst_retry:
|
|||
cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_SERVER_INUM;
|
||||
goto ffirst_retry;
|
||||
}
|
||||
error_exit:
|
||||
kfree(full_path);
|
||||
cifs_put_tlink(tlink);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -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,6 @@ 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;
|
||||
|
||||
switch ((int) file->f_pos) {
|
||||
case 0:
|
||||
|
@ -829,6 +838,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) {
|
||||
|
|
134
fs/cifs/sess.c
134
fs/cifs/sess.c
|
@ -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)) {
|
||||
|
@ -405,6 +408,19 @@ static int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
|
|||
/* 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;
|
||||
|
@ -449,12 +465,14 @@ static void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
|
|||
This function returns the length of the data in the blob */
|
||||
static int build_ntlmssp_auth_blob(unsigned char *pbuffer,
|
||||
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 +480,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 +495,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 +526,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 +542,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,6 +556,8 @@ 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;
|
||||
|
||||
setup_ntlmv2_ret:
|
||||
return tmp - pbuffer;
|
||||
}
|
||||
|
||||
|
@ -545,19 +570,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
|
||||
|
@ -580,6 +592,8 @@ CIFS_SessSetup(unsigned int xid, struct cifsSesInfo *ses,
|
|||
struct key *spnego_key = NULL;
|
||||
__le32 phase = NtLmNegotiate; /* NTLMSSP, if needed, is multistage */
|
||||
bool first_time;
|
||||
int blob_len;
|
||||
char *ntlmsspblob = NULL;
|
||||
|
||||
if (ses == NULL)
|
||||
return -EINVAL;
|
||||
|
@ -690,7 +704,7 @@ ssetup_ntlmssp_authenticate:
|
|||
|
||||
if (first_time) /* should this be moved into common code
|
||||
with similar ntlmv2 path? */
|
||||
cifs_calculate_mac_key(&ses->server->mac_signing_key,
|
||||
cifs_calculate_session_key(&ses->server->session_key,
|
||||
ntlm_session_key, ses->password);
|
||||
/* copy session key */
|
||||
|
||||
|
@ -725,16 +739,31 @@ ssetup_ntlmssp_authenticate:
|
|||
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));
|
||||
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,15 +794,15 @@ ssetup_ntlmssp_authenticate:
|
|||
}
|
||||
/* bail out if key is too long */
|
||||
if (msg->sesskey_len >
|
||||
sizeof(ses->server->mac_signing_key.data.krb5)) {
|
||||
sizeof(ses->server->session_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,
|
||||
ses->server->session_key.len = msg->sesskey_len;
|
||||
memcpy(ses->server->session_key.data.krb5,
|
||||
msg->data, msg->sesskey_len);
|
||||
}
|
||||
pSMB->req.hdr.Flags2 |= SMBFLG2_EXT_SEC;
|
||||
|
@ -815,12 +844,28 @@ ssetup_ntlmssp_authenticate:
|
|||
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;
|
||||
}
|
||||
|
||||
blob_len = build_ntlmssp_auth_blob(ntlmsspblob,
|
||||
ses, nls_cp);
|
||||
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 +875,6 @@ ssetup_ntlmssp_authenticate:
|
|||
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;
|
||||
|
@ -931,6 +975,8 @@ ssetup_exit:
|
|||
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 New Issue