diff options
Diffstat (limited to 'fs/cifs')
35 files changed, 2377 insertions, 1047 deletions
diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h index 37e4a72a7d1..9409fa10bd5 100644 --- a/fs/cifs/cifs_fs_sb.h +++ b/fs/cifs/cifs_fs_sb.h @@ -65,5 +65,6 @@ struct cifs_sb_info {  	char   *mountdata; /* options received at mount time or via DFS refs */  	struct backing_dev_info bdi;  	struct delayed_work prune_tlinks; +	struct rcu_head rcu;  };  #endif				/* _CIFS_FS_SB_H */ diff --git a/fs/cifs/cifs_unicode.c b/fs/cifs/cifs_unicode.c index 0227b45ef00..15e9505aa35 100644 --- a/fs/cifs/cifs_unicode.c +++ b/fs/cifs/cifs_unicode.c @@ -290,7 +290,8 @@ int  cifsConvertToUTF16(__le16 *target, const char *source, int srclen,  		 const struct nls_table *cp, int mapChars)  { -	int i, j, charlen; +	int i, charlen; +	int j = 0;  	char src_char;  	__le16 dst_char;  	wchar_t tmp; @@ -298,12 +299,11 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,  	if (!mapChars)  		return cifs_strtoUTF16(target, source, PATH_MAX, cp); -	for (i = 0, j = 0; i < srclen; j++) { +	for (i = 0; i < srclen; j++) {  		src_char = source[i];  		charlen = 1;  		switch (src_char) {  		case 0: -			put_unaligned(0, &target[j]);  			goto ctoUTF16_out;  		case ':':  			dst_char = cpu_to_le16(UNI_COLON); @@ -350,6 +350,7 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,  	}  ctoUTF16_out: +	put_unaligned(0, &target[j]); /* Null terminate target unicode string */  	return j;  } diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 51f5e0ee723..7ff866dbb89 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -865,8 +865,8 @@ static int build_sec_desc(struct cifs_ntsd *pntsd, struct cifs_ntsd *pnntsd,  	return rc;  } -static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, -		__u16 fid, u32 *pacllen) +struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb, +		const struct cifs_fid *cifsfid, u32 *pacllen)  {  	struct cifs_ntsd *pntsd = NULL;  	unsigned int xid; @@ -877,7 +877,8 @@ static struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *cifs_sb,  		return ERR_CAST(tlink);  	xid = get_xid(); -	rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), fid, &pntsd, pacllen); +	rc = CIFSSMBGetCIFSACL(xid, tlink_tcon(tlink), cifsfid->netfid, &pntsd, +				pacllen);  	free_xid(xid);  	cifs_put_tlink(tlink); @@ -895,9 +896,10 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,  	int oplock = 0;  	unsigned int xid;  	int rc, create_options = 0; -	__u16 fid;  	struct cifs_tcon *tcon;  	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	if (IS_ERR(tlink))  		return ERR_CAST(tlink); @@ -908,12 +910,19 @@ static struct cifs_ntsd *get_cifs_acl_by_path(struct cifs_sb_info *cifs_sb,  	if (backup_cred(cifs_sb))  		create_options |= CREATE_OPEN_BACKUP_INTENT; -	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, READ_CONTROL, -			create_options, &fid, &oplock, NULL, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = READ_CONTROL; +	oparms.create_options = create_options; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (!rc) { -		rc = CIFSSMBGetCIFSACL(xid, tcon, fid, &pntsd, pacllen); -		CIFSSMBClose(xid, tcon, fid); +		rc = CIFSSMBGetCIFSACL(xid, tcon, fid.netfid, &pntsd, pacllen); +		CIFSSMBClose(xid, tcon, fid.netfid);  	}  	cifs_put_tlink(tlink); @@ -938,7 +947,7 @@ struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *cifs_sb,  	if (!open_file)  		return get_cifs_acl_by_path(cifs_sb, path, pacllen); -	pntsd = get_cifs_acl_by_fid(cifs_sb, open_file->fid.netfid, pacllen); +	pntsd = get_cifs_acl_by_fid(cifs_sb, &open_file->fid, pacllen);  	cifsFileInfo_put(open_file);  	return pntsd;  } @@ -950,10 +959,11 @@ int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,  	int oplock = 0;  	unsigned int xid;  	int rc, access_flags, create_options = 0; -	__u16 fid;  	struct cifs_tcon *tcon;  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);  	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); @@ -969,18 +979,25 @@ int set_cifs_acl(struct cifs_ntsd *pnntsd, __u32 acllen,  	else  		access_flags = WRITE_DAC; -	rc = CIFSSMBOpen(xid, tcon, path, FILE_OPEN, access_flags, -			create_options, &fid, &oplock, NULL, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = access_flags; +	oparms.create_options = create_options; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc) {  		cifs_dbg(VFS, "Unable to open file to set ACL\n");  		goto out;  	} -	rc = CIFSSMBSetCIFSACL(xid, tcon, fid, pnntsd, acllen, aclflag); +	rc = CIFSSMBSetCIFSACL(xid, tcon, fid.netfid, pnntsd, acllen, aclflag);  	cifs_dbg(NOISY, "SetCIFSACL rc = %d\n", rc); -	CIFSSMBClose(xid, tcon, fid); +	CIFSSMBClose(xid, tcon, fid.netfid);  out:  	free_xid(xid);  	cifs_put_tlink(tlink); @@ -990,19 +1007,31 @@ out:  /* Translate the CIFS ACL (simlar to NTFS ACL) for a file into mode bits */  int  cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, -		  struct inode *inode, const char *path, const __u16 *pfid) +		  struct inode *inode, const char *path, +		  const struct cifs_fid *pfid)  {  	struct cifs_ntsd *pntsd = NULL;  	u32 acllen = 0;  	int rc = 0; +	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_tcon *tcon;  	cifs_dbg(NOISY, "converting ACL to mode for %s\n", path); -	if (pfid) -		pntsd = get_cifs_acl_by_fid(cifs_sb, *pfid, &acllen); -	else -		pntsd = get_cifs_acl(cifs_sb, inode, path, &acllen); +	if (IS_ERR(tlink)) +		return PTR_ERR(tlink); +	tcon = tlink_tcon(tlink); +	if (pfid && (tcon->ses->server->ops->get_acl_by_fid)) +		pntsd = tcon->ses->server->ops->get_acl_by_fid(cifs_sb, pfid, +							  &acllen); +	else if (tcon->ses->server->ops->get_acl) +		pntsd = tcon->ses->server->ops->get_acl(cifs_sb, inode, path, +							&acllen); +	else { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	}  	/* if we can retrieve the ACL, now parse Access Control Entries, ACEs */  	if (IS_ERR(pntsd)) {  		rc = PTR_ERR(pntsd); @@ -1014,6 +1043,8 @@ cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr,  			cifs_dbg(VFS, "parse sec desc failed rc = %d\n", rc);  	} +	cifs_put_tlink(tlink); +  	return rc;  } @@ -1027,15 +1058,30 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,  	__u32 secdesclen = 0;  	struct cifs_ntsd *pntsd = NULL; /* acl obtained from server */  	struct cifs_ntsd *pnntsd = NULL; /* modified acl to be sent to server */ +	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); +	struct tcon_link *tlink = cifs_sb_tlink(cifs_sb); +	struct cifs_tcon *tcon; + +	if (IS_ERR(tlink)) +		return PTR_ERR(tlink); +	tcon = tlink_tcon(tlink);  	cifs_dbg(NOISY, "set ACL from mode for %s\n", path);  	/* Get the security descriptor */ -	pntsd = get_cifs_acl(CIFS_SB(inode->i_sb), inode, path, &secdesclen); + +	if (tcon->ses->server->ops->get_acl == NULL) { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	} + +	pntsd = tcon->ses->server->ops->get_acl(cifs_sb, inode, path, +						&secdesclen);  	if (IS_ERR(pntsd)) {  		rc = PTR_ERR(pntsd);  		cifs_dbg(VFS, "%s: error %d getting sec desc\n", __func__, rc); -		goto out; +		cifs_put_tlink(tlink); +		return rc;  	}  	/* @@ -1048,6 +1094,7 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,  	pnntsd = kmalloc(secdesclen, GFP_KERNEL);  	if (!pnntsd) {  		kfree(pntsd); +		cifs_put_tlink(tlink);  		return -ENOMEM;  	} @@ -1056,14 +1103,18 @@ id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64 nmode,  	cifs_dbg(NOISY, "build_sec_desc rc: %d\n", rc); +	if (tcon->ses->server->ops->set_acl == NULL) +		rc = -EOPNOTSUPP; +  	if (!rc) {  		/* Set the security descriptor */ -		rc = set_cifs_acl(pnntsd, secdesclen, inode, path, aclflag); +		rc = tcon->ses->server->ops->set_acl(pnntsd, secdesclen, inode, +						     path, aclflag);  		cifs_dbg(NOISY, "set_cifs_acl rc: %d\n", rc);  	} +	cifs_put_tlink(tlink);  	kfree(pnntsd);  	kfree(pntsd); -out:  	return rc;  } diff --git a/fs/cifs/cifsencrypt.c b/fs/cifs/cifsencrypt.c index fc6f4f3a1a9..4934347321d 100644 --- a/fs/cifs/cifsencrypt.c +++ b/fs/cifs/cifsencrypt.c @@ -548,7 +548,13 @@ static int  CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)  {  	int rc; -	unsigned int offset = CIFS_SESS_KEY_SIZE + 8; +	struct ntlmv2_resp *ntlmv2 = (struct ntlmv2_resp *) +	    (ses->auth_key.response + CIFS_SESS_KEY_SIZE); +	unsigned int hash_len; + +	/* The MD5 hash starts at challenge_key.key */ +	hash_len = ses->auth_key.len - (CIFS_SESS_KEY_SIZE + +		offsetof(struct ntlmv2_resp, challenge.key[0]));  	if (!ses->server->secmech.sdeschmacmd5) {  		cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); @@ -556,7 +562,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)  	}  	rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, -				ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); +				 ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);  	if (rc) {  		cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",  			 __func__); @@ -570,20 +576,21 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)  	}  	if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) -		memcpy(ses->auth_key.response + offset, -			ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); +		memcpy(ntlmv2->challenge.key, +		       ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);  	else -		memcpy(ses->auth_key.response + offset, -			ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); +		memcpy(ntlmv2->challenge.key, +		       ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);  	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + offset, ses->auth_key.len - offset); +				 ntlmv2->challenge.key, hash_len);  	if (rc) {  		cifs_dbg(VFS, "%s: Could not update with response\n", __func__);  		return rc;  	} +	/* Note that the MD5 digest over writes anon.challenge_key.key */  	rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + CIFS_SESS_KEY_SIZE); +				ntlmv2->ntlmv2_hash);  	if (rc)  		cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); @@ -627,7 +634,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)  	int rc;  	int baselen;  	unsigned int tilen; -	struct ntlmv2_resp *buf; +	struct ntlmv2_resp *ntlmv2;  	char ntlmv2_hash[16];  	unsigned char *tiblob = NULL; /* target info blob */ @@ -660,13 +667,14 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)  	}  	ses->auth_key.len += baselen; -	buf = (struct ntlmv2_resp *) +	ntlmv2 = (struct ntlmv2_resp *)  			(ses->auth_key.response + CIFS_SESS_KEY_SIZE); -	buf->blob_signature = cpu_to_le32(0x00000101); -	buf->reserved = 0; -	buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); -	get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); -	buf->reserved2 = 0; +	ntlmv2->blob_signature = cpu_to_le32(0x00000101); +	ntlmv2->reserved = 0; +	/* Must be within 5 minutes of the server */ +	ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); +	get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal)); +	ntlmv2->reserved2 = 0;  	memcpy(ses->auth_key.response + baselen, tiblob, tilen); @@ -706,7 +714,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)  	}  	rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, -		ses->auth_key.response + CIFS_SESS_KEY_SIZE, +		ntlmv2->ntlmv2_hash,  		CIFS_HMAC_MD5_HASH_SIZE);  	if (rc) {  		cifs_dbg(VFS, "%s: Could not update with response\n", __func__); diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c index a16b4e58bcc..88839806742 100644 --- a/fs/cifs/cifsfs.c +++ b/fs/cifs/cifsfs.c @@ -87,10 +87,6 @@ extern mempool_t *cifs_mid_poolp;  struct workqueue_struct	*cifsiod_wq; -#ifdef CONFIG_CIFS_SMB2 -__u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; -#endif -  /*   * Bumps refcount for cifs super block.   * Note that it should be only called if a referece to VFS super block is @@ -120,14 +116,16 @@ cifs_read_super(struct super_block *sb)  {  	struct inode *inode;  	struct cifs_sb_info *cifs_sb; +	struct cifs_tcon *tcon;  	int rc = 0;  	cifs_sb = CIFS_SB(sb); +	tcon = cifs_sb_master_tcon(cifs_sb);  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIXACL)  		sb->s_flags |= MS_POSIXACL; -	if (cifs_sb_master_tcon(cifs_sb)->ses->capabilities & CAP_LARGE_FILES) +	if (tcon->ses->capabilities & tcon->ses->server->vals->cap_large_files)  		sb->s_maxbytes = MAX_LFS_FILESIZE;  	else  		sb->s_maxbytes = MAX_NON_LFS; @@ -147,7 +145,7 @@ cifs_read_super(struct super_block *sb)  		goto out_no_root;  	} -	if (cifs_sb_master_tcon(cifs_sb)->nocase) +	if (tcon->nocase)  		sb->s_d_op = &cifs_ci_dentry_ops;  	else  		sb->s_d_op = &cifs_dentry_ops; @@ -249,8 +247,9 @@ cifs_alloc_inode(struct super_block *sb)  	 * server, can not assume caching of file data or metadata.  	 */  	cifs_set_oplock_level(cifs_inode, 0); -	cifs_inode->delete_pending = false; -	cifs_inode->invalid_mapping = false; +	cifs_inode->flags = 0; +	spin_lock_init(&cifs_inode->writers_lock); +	cifs_inode->writers = 0;  	cifs_inode->vfs_inode.i_blkbits = 14;  /* 2**14 = CIFS_MAX_MSGSIZE */  	cifs_inode->server_eof = 0;  	cifs_inode->uniqueid = 0; @@ -284,7 +283,7 @@ cifs_destroy_inode(struct inode *inode)  static void  cifs_evict_inode(struct inode *inode)  { -	truncate_inode_pages(&inode->i_data, 0); +	truncate_inode_pages_final(&inode->i_data);  	clear_inode(inode);  	cifs_fscache_release_inode_cookie(inode);  } @@ -295,7 +294,7 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)  	struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;  	struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; -	seq_printf(s, ",addr="); +	seq_puts(s, ",addr=");  	switch (server->dstaddr.ss_family) {  	case AF_INET: @@ -307,7 +306,7 @@ cifs_show_address(struct seq_file *s, struct TCP_Server_Info *server)  			seq_printf(s, "%%%u", sa6->sin6_scope_id);  		break;  	default: -		seq_printf(s, "(unknown)"); +		seq_puts(s, "(unknown)");  	}  } @@ -317,45 +316,45 @@ cifs_show_security(struct seq_file *s, struct cifs_ses *ses)  	if (ses->sectype == Unspecified)  		return; -	seq_printf(s, ",sec="); +	seq_puts(s, ",sec=");  	switch (ses->sectype) {  	case LANMAN: -		seq_printf(s, "lanman"); +		seq_puts(s, "lanman");  		break;  	case NTLMv2: -		seq_printf(s, "ntlmv2"); +		seq_puts(s, "ntlmv2");  		break;  	case NTLM: -		seq_printf(s, "ntlm"); +		seq_puts(s, "ntlm");  		break;  	case Kerberos: -		seq_printf(s, "krb5"); +		seq_puts(s, "krb5");  		break;  	case RawNTLMSSP: -		seq_printf(s, "ntlmssp"); +		seq_puts(s, "ntlmssp");  		break;  	default:  		/* shouldn't ever happen */ -		seq_printf(s, "unknown"); +		seq_puts(s, "unknown");  		break;  	}  	if (ses->sign) -		seq_printf(s, "i"); +		seq_puts(s, "i");  }  static void  cifs_show_cache_flavor(struct seq_file *s, struct cifs_sb_info *cifs_sb)  { -	seq_printf(s, ",cache="); +	seq_puts(s, ",cache=");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) -		seq_printf(s, "strict"); +		seq_puts(s, "strict");  	else if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DIRECT_IO) -		seq_printf(s, "none"); +		seq_puts(s, "none");  	else -		seq_printf(s, "loose"); +		seq_puts(s, "loose");  }  static void @@ -388,7 +387,7 @@ cifs_show_options(struct seq_file *s, struct dentry *root)  	cifs_show_cache_flavor(s, cifs_sb);  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER) -		seq_printf(s, ",multiuser"); +		seq_puts(s, ",multiuser");  	else if (tcon->ses->user_name)  		seq_printf(s, ",username=%s", tcon->ses->user_name); @@ -414,16 +413,16 @@ cifs_show_options(struct seq_file *s, struct dentry *root)  	seq_printf(s, ",uid=%u",  		   from_kuid_munged(&init_user_ns, cifs_sb->mnt_uid));  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_UID) -		seq_printf(s, ",forceuid"); +		seq_puts(s, ",forceuid");  	else -		seq_printf(s, ",noforceuid"); +		seq_puts(s, ",noforceuid");  	seq_printf(s, ",gid=%u",  		   from_kgid_munged(&init_user_ns, cifs_sb->mnt_gid));  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_OVERR_GID) -		seq_printf(s, ",forcegid"); +		seq_puts(s, ",forcegid");  	else -		seq_printf(s, ",noforcegid"); +		seq_puts(s, ",noforcegid");  	cifs_show_address(s, tcon->ses->server); @@ -435,47 +434,47 @@ cifs_show_options(struct seq_file *s, struct dentry *root)  	cifs_show_nls(s, cifs_sb->local_nls);  	if (tcon->seal) -		seq_printf(s, ",seal"); +		seq_puts(s, ",seal");  	if (tcon->nocase) -		seq_printf(s, ",nocase"); +		seq_puts(s, ",nocase");  	if (tcon->retry) -		seq_printf(s, ",hard"); +		seq_puts(s, ",hard");  	if (tcon->unix_ext) -		seq_printf(s, ",unix"); +		seq_puts(s, ",unix");  	else -		seq_printf(s, ",nounix"); +		seq_puts(s, ",nounix");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) -		seq_printf(s, ",posixpaths"); +		seq_puts(s, ",posixpaths");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) -		seq_printf(s, ",setuids"); +		seq_puts(s, ",setuids");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) -		seq_printf(s, ",serverino"); +		seq_puts(s, ",serverino");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RWPIDFORWARD) -		seq_printf(s, ",rwpidforward"); +		seq_puts(s, ",rwpidforward");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) -		seq_printf(s, ",forcemand"); +		seq_puts(s, ",forcemand");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR) -		seq_printf(s, ",nouser_xattr"); +		seq_puts(s, ",nouser_xattr");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR) -		seq_printf(s, ",mapchars"); +		seq_puts(s, ",mapchars");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) -		seq_printf(s, ",sfu"); +		seq_puts(s, ",sfu");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_BRL) -		seq_printf(s, ",nobrl"); +		seq_puts(s, ",nobrl");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) -		seq_printf(s, ",cifsacl"); +		seq_puts(s, ",cifsacl");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_DYNPERM) -		seq_printf(s, ",dynperm"); +		seq_puts(s, ",dynperm");  	if (root->d_sb->s_flags & MS_POSIXACL) -		seq_printf(s, ",acl"); +		seq_puts(s, ",acl");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -		seq_printf(s, ",mfsymlinks"); +		seq_puts(s, ",mfsymlinks");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) -		seq_printf(s, ",fsc"); +		seq_puts(s, ",fsc");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOSSYNC) -		seq_printf(s, ",nostrictsync"); +		seq_puts(s, ",nostrictsync");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_PERM) -		seq_printf(s, ",noperm"); +		seq_puts(s, ",noperm");  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_BACKUPUID)  		seq_printf(s, ",backupuid=%u",  			   from_kuid_munged(&init_user_ns, @@ -539,6 +538,7 @@ static int cifs_show_stats(struct seq_file *s, struct dentry *root)  static int cifs_remount(struct super_block *sb, int *flags, char *data)  { +	sync_filesystem(sb);  	*flags |= MS_NODIRATIME;  	return 0;  } @@ -725,23 +725,42 @@ out_nls:  	goto out;  } -static ssize_t cifs_file_aio_write(struct kiocb *iocb, const struct iovec *iov, -				   unsigned long nr_segs, loff_t pos) +static ssize_t +cifs_loose_read_iter(struct kiocb *iocb, struct iov_iter *iter) +{ +	ssize_t rc; +	struct inode *inode = file_inode(iocb->ki_filp); + +	rc = cifs_revalidate_mapping(inode); +	if (rc) +		return rc; + +	return generic_file_read_iter(iocb, iter); +} + +static ssize_t cifs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)  {  	struct inode *inode = file_inode(iocb->ki_filp); +	struct cifsInodeInfo *cinode = CIFS_I(inode);  	ssize_t written;  	int rc; -	written = generic_file_aio_write(iocb, iov, nr_segs, pos); +	written = cifs_get_writer(cinode); +	if (written) +		return written; + +	written = generic_file_write_iter(iocb, from);  	if (CIFS_CACHE_WRITE(CIFS_I(inode))) -		return written; +		goto out;  	rc = filemap_fdatawrite(inode->i_mapping);  	if (rc) -		cifs_dbg(FYI, "cifs_file_aio_write: %d rc on %p inode\n", +		cifs_dbg(FYI, "cifs_file_write_iter: %d rc on %p inode\n",  			 rc, inode); +out: +	cifs_put_writer(cinode);  	return written;  } @@ -847,7 +866,6 @@ const struct inode_operations cifs_file_inode_ops = {  /*	revalidate:cifs_revalidate, */  	.setattr = cifs_setattr,  	.getattr = cifs_getattr, /* do we need this anymore? */ -	.rename = cifs_rename,  	.permission = cifs_permission,  #ifdef CONFIG_CIFS_XATTR  	.setxattr = cifs_setxattr, @@ -860,7 +878,7 @@ const struct inode_operations cifs_file_inode_ops = {  const struct inode_operations cifs_symlink_inode_ops = {  	.readlink = generic_readlink,  	.follow_link = cifs_follow_link, -	.put_link = cifs_put_link, +	.put_link = kfree_put_link,  	.permission = cifs_permission,  	/* BB add the following two eventually */  	/* revalidate: cifs_revalidate, @@ -874,10 +892,10 @@ const struct inode_operations cifs_symlink_inode_ops = {  };  const struct file_operations cifs_file_ops = { -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = generic_file_aio_read, -	.aio_write = cifs_file_aio_write, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_loose_read_iter, +	.write_iter = cifs_file_write_iter,  	.open = cifs_open,  	.release = cifs_close,  	.lock = cifs_lock, @@ -893,10 +911,10 @@ const struct file_operations cifs_file_ops = {  };  const struct file_operations cifs_file_strict_ops = { -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = cifs_strict_readv, -	.aio_write = cifs_strict_writev, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_strict_readv, +	.write_iter = cifs_strict_writev,  	.open = cifs_open,  	.release = cifs_close,  	.lock = cifs_lock, @@ -913,10 +931,10 @@ const struct file_operations cifs_file_strict_ops = {  const struct file_operations cifs_file_direct_ops = {  	/* BB reevaluate whether they can be done with directio, no cache */ -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = cifs_user_readv, -	.aio_write = cifs_user_writev, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_user_readv, +	.write_iter = cifs_user_writev,  	.open = cifs_open,  	.release = cifs_close,  	.lock = cifs_lock, @@ -932,10 +950,10 @@ const struct file_operations cifs_file_direct_ops = {  };  const struct file_operations cifs_file_nobrl_ops = { -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = generic_file_aio_read, -	.aio_write = cifs_file_aio_write, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_loose_read_iter, +	.write_iter = cifs_file_write_iter,  	.open = cifs_open,  	.release = cifs_close,  	.fsync = cifs_fsync, @@ -950,10 +968,10 @@ const struct file_operations cifs_file_nobrl_ops = {  };  const struct file_operations cifs_file_strict_nobrl_ops = { -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = cifs_strict_readv, -	.aio_write = cifs_strict_writev, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_strict_readv, +	.write_iter = cifs_strict_writev,  	.open = cifs_open,  	.release = cifs_close,  	.fsync = cifs_strict_fsync, @@ -969,10 +987,10 @@ const struct file_operations cifs_file_strict_nobrl_ops = {  const struct file_operations cifs_file_direct_nobrl_ops = {  	/* BB reevaluate whether they can be done with directio, no cache */ -	.read = do_sync_read, -	.write = do_sync_write, -	.aio_read = cifs_user_readv, -	.aio_write = cifs_user_writev, +	.read = new_sync_read, +	.write = new_sync_write, +	.read_iter = cifs_user_readv, +	.write_iter = cifs_user_writev,  	.open = cifs_open,  	.release = cifs_close,  	.fsync = cifs_fsync, @@ -1003,7 +1021,7 @@ cifs_init_once(void *inode)  	init_rwsem(&cifsi->lock_sem);  } -static int +static int __init  cifs_init_inodecache(void)  {  	cifs_inode_cachep = kmem_cache_create("cifs_inode_cache", @@ -1178,10 +1196,6 @@ init_cifs(void)  	spin_lock_init(&cifs_file_list_lock);  	spin_lock_init(&GlobalMid_Lock); -#ifdef CONFIG_CIFS_SMB2 -	get_random_bytes(cifs_client_guid, SMB2_CLIENT_GUID_SIZE); -#endif -  	if (cifs_max_pending < 2) {  		cifs_max_pending = 2;  		cifs_dbg(FYI, "cifs_max_pending set to min of 2\n"); diff --git a/fs/cifs/cifsfs.h b/fs/cifs/cifsfs.h index ea723a5e822..70f178a7c75 100644 --- a/fs/cifs/cifsfs.h +++ b/fs/cifs/cifsfs.h @@ -22,20 +22,28 @@  #ifndef _CIFSFS_H  #define _CIFSFS_H +#include <linux/hash.h> +  #define ROOT_I 2  /*   * ino_t is 32-bits on 32-bit arch. We have to squash the 64-bit value down - * so that it will fit. + * so that it will fit. We use hash_64 to convert the value to 31 bits, and + * then add 1, to ensure that we don't end up with a 0 as the value.   */ +#if BITS_PER_LONG == 64  static inline ino_t  cifs_uniqueid_to_ino_t(u64 fileid)  { -	ino_t ino = (ino_t) fileid; -	if (sizeof(ino_t) < sizeof(u64)) -		ino ^= fileid >> (sizeof(u64)-sizeof(ino_t)) * 8; -	return ino; +	return (ino_t)fileid;  } +#else +static inline ino_t +cifs_uniqueid_to_ino_t(u64 fileid) +{ +	return (ino_t)hash_64(fileid, (sizeof(ino_t) * 8) - 1) + 1; +} +#endif  extern struct file_system_type cifs_fs_type;  extern const struct address_space_operations cifs_addr_ops; @@ -67,6 +75,8 @@ extern int cifs_revalidate_dentry_attr(struct dentry *);  extern int cifs_revalidate_file(struct file *filp);  extern int cifs_revalidate_dentry(struct dentry *);  extern int cifs_invalidate_mapping(struct inode *inode); +extern int cifs_revalidate_mapping(struct inode *inode); +extern int cifs_zap_mapping(struct inode *inode);  extern int cifs_getattr(struct vfsmount *, struct dentry *, struct kstat *);  extern int cifs_setattr(struct dentry *, struct iattr *); @@ -85,14 +95,10 @@ extern const struct file_operations cifs_file_strict_nobrl_ops;  extern int cifs_open(struct inode *inode, struct file *file);  extern int cifs_close(struct inode *inode, struct file *file);  extern int cifs_closedir(struct inode *inode, struct file *file); -extern ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, -			       unsigned long nr_segs, loff_t pos); -extern ssize_t cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, -				 unsigned long nr_segs, loff_t pos); -extern ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, -				unsigned long nr_segs, loff_t pos); -extern ssize_t cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, -				  unsigned long nr_segs, loff_t pos); +extern ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to); +extern ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from); +extern ssize_t cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from);  extern int cifs_lock(struct file *, int, struct file_lock *);  extern int cifs_fsync(struct file *, loff_t, loff_t, int);  extern int cifs_strict_fsync(struct file *, loff_t, loff_t, int); @@ -115,8 +121,6 @@ extern struct vfsmount *cifs_dfs_d_automount(struct path *path);  /* Functions related to symlinks */  extern void *cifs_follow_link(struct dentry *direntry, struct nameidata *nd); -extern void cifs_put_link(struct dentry *direntry, -			  struct nameidata *nd, void *);  extern int cifs_readlink(struct dentry *direntry, char __user *buffer,  			 int buflen);  extern int cifs_symlink(struct inode *inode, struct dentry *direntry, @@ -132,5 +136,5 @@ extern long cifs_ioctl(struct file *filep, unsigned int cmd, unsigned long arg);  extern const struct export_operations cifs_export_ops;  #endif /* CONFIG_CIFS_NFSD_EXPORT */ -#define CIFS_VERSION   "2.01" +#define CIFS_VERSION   "2.03"  #endif				/* _CIFSFS_H */ diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index cfa14c80ef3..de6aed8c78e 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -228,6 +228,8 @@ struct smb_version_operations {  	/* verify the message */  	int (*check_message)(char *, unsigned int);  	bool (*is_oplock_break)(char *, struct TCP_Server_Info *); +	void (*downgrade_oplock)(struct TCP_Server_Info *, +					struct cifsInodeInfo *, bool);  	/* process transaction2 response */  	bool (*check_trans2)(struct mid_q_entry *, struct TCP_Server_Info *,  			     char *, int); @@ -261,7 +263,7 @@ struct smb_version_operations {  	/* query path data from the server */  	int (*query_path_info)(const unsigned int, struct cifs_tcon *,  			       struct cifs_sb_info *, const char *, -			       FILE_ALL_INFO *, bool *); +			       FILE_ALL_INFO *, bool *, bool *);  	/* query file data from the server */  	int (*query_file_info)(const unsigned int, struct cifs_tcon *,  			       struct cifs_fid *, FILE_ALL_INFO *); @@ -278,6 +280,8 @@ struct smb_version_operations {  	/* set attributes */  	int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,  			     const unsigned int); +	int (*set_compression)(const unsigned int, struct cifs_tcon *, +			       struct cifsFileInfo *);  	/* check if we can send an echo or nor */  	bool (*can_echo)(struct TCP_Server_Info *);  	/* send echo request */ @@ -321,7 +325,8 @@ struct smb_version_operations {  	/* async read from the server */  	int (*async_readv)(struct cifs_readdata *);  	/* async write to the server */ -	int (*async_writev)(struct cifs_writedata *); +	int (*async_writev)(struct cifs_writedata *, +			    void (*release)(struct kref *));  	/* sync read from the server */  	int (*sync_read)(const unsigned int, struct cifsFileInfo *,  			 struct cifs_io_parms *, unsigned int *, char **, @@ -368,8 +373,12 @@ struct smb_version_operations {  	void (*new_lease_key)(struct cifs_fid *);  	int (*generate_signingkey)(struct cifs_ses *);  	int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *); -	int (*query_mf_symlink)(const unsigned char *, char *, unsigned int *, -				struct cifs_sb_info *, unsigned int); +	int (*query_mf_symlink)(unsigned int, struct cifs_tcon *, +				struct cifs_sb_info *, const unsigned char *, +				char *, unsigned int *); +	int (*create_mf_symlink)(unsigned int, struct cifs_tcon *, +				 struct cifs_sb_info *, const unsigned char *, +				 char *, unsigned int *);  	/* if we can do cache read operations */  	bool (*is_read_op)(__u32);  	/* set oplock level for the inode */ @@ -379,6 +388,22 @@ struct smb_version_operations {  	char * (*create_lease_buf)(u8 *, u8);  	/* parse lease context buffer and return oplock/epoch info */  	__u8 (*parse_lease_buf)(void *, unsigned int *); +	int (*clone_range)(const unsigned int, struct cifsFileInfo *src_file, +			struct cifsFileInfo *target_file, u64 src_off, u64 len, +			u64 dest_off); +	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *); +	ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *, +			const unsigned char *, const unsigned char *, char *, +			size_t, const struct nls_table *, int); +	int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *, +			const char *, const void *, const __u16, +			const struct nls_table *, int); +	struct cifs_ntsd * (*get_acl)(struct cifs_sb_info *, struct inode *, +			const char *, u32 *); +	struct cifs_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *, +			const struct cifs_fid *, u32 *); +	int (*set_acl)(struct cifs_ntsd *, __u32, struct inode *, const char *, +			int);  };  struct smb_version_values { @@ -490,7 +515,7 @@ struct cifs_mnt_data {  static inline unsigned int  get_rfc1002_length(void *buf)  { -	return be32_to_cpu(*((__be32 *)buf)); +	return be32_to_cpu(*((__be32 *)buf)) & 0xffffff;  }  static inline void @@ -534,6 +559,7 @@ struct TCP_Server_Info {  	int echo_credits;  /* echo reserved slots */  	int oplock_credits;  /* oplock break reserved slots */  	bool echoes:1; /* enable echoes */ +	__u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* Client GUID */  #endif  	u16 dialect; /* dialect index that server chose */  	bool oplocks:1; /* enable oplocks */ @@ -547,9 +573,6 @@ struct TCP_Server_Info {  	unsigned int max_rw;	/* maxRw specifies the maximum */  	/* message size the server can send or receive for */  	/* SMB_COM_WRITE_RAW or SMB_COM_READ_RAW. */ -	unsigned int max_vcs;	/* maximum number of smb sessions, at least -				   those that can be specified uniquely with -				   vcnumbers */  	unsigned int capabilities; /* selective disabling of caps by smb sess */  	int timeAdj;  /* Adjust for difference in server time zone in sec */  	__u64 CurrentMid;         /* multiplex id - rotating counter */ @@ -623,11 +646,34 @@ set_credits(struct TCP_Server_Info *server, const int val)  }  static inline __u64 -get_next_mid(struct TCP_Server_Info *server) +get_next_mid64(struct TCP_Server_Info *server)  {  	return server->ops->get_next_mid(server);  } +static inline __le16 +get_next_mid(struct TCP_Server_Info *server) +{ +	__u16 mid = get_next_mid64(server); +	/* +	 * The value in the SMB header should be little endian for easy +	 * on-the-wire decoding. +	 */ +	return cpu_to_le16(mid); +} + +static inline __u16 +get_mid(const struct smb_hdr *smb) +{ +	return le16_to_cpu(smb->Mid); +} + +static inline bool +compare_mid(__u16 mid, const struct smb_hdr *smb) +{ +	return mid == le16_to_cpu(smb->Mid); +} +  /*   * When the server supports very large reads and writes via POSIX extensions,   * we can allow up to 2^24-1, minus the size of a READ/WRITE_AND_X header, not @@ -715,7 +761,6 @@ struct cifs_ses {  	enum statusEnum status;  	unsigned overrideSecFlg;  /* if non-zero override global sec flags */  	__u16 ipc_tid;		/* special tid for connection to IPC share */ -	__u16 vcnum;  	char *serverOS;		/* name of operating system underlying server */  	char *serverNOS;	/* name of network operating system of server */  	char *serverDomain;	/* security realm of server */ @@ -832,6 +877,11 @@ struct cifs_tcon {  	__u32 maximal_access;  	__u32 vol_serial_number;  	__le64 vol_create_time; +	__u32 ss_flags;		/* sector size flags */ +	__u32 perf_sector_size; /* best sector size for perf */ +	__u32 max_chunks; +	__u32 max_bytes_chunk; +	__u32 max_bytes_copy;  #endif /* CONFIG_CIFS_SMB2 */  #ifdef CONFIG_CIFS_FSCACHE  	u64 resource_id;		/* server resource id */ @@ -1024,7 +1074,7 @@ struct cifs_writedata {  	unsigned int			pagesz;  	unsigned int			tailsz;  	unsigned int			nr_pages; -	struct page			*pages[1]; +	struct page			*pages[];  };  /* @@ -1064,8 +1114,15 @@ struct cifsInodeInfo {  	__u32 cifsAttrs; /* e.g. DOS archive bit, sparse, compressed, system */  	unsigned int oplock;		/* oplock/lease level we have */  	unsigned int epoch;		/* used to track lease state changes */ -	bool delete_pending;		/* DELETE_ON_CLOSE is set */ -	bool invalid_mapping;		/* pagecache is invalid */ +#define CIFS_INODE_PENDING_OPLOCK_BREAK   (0) /* oplock break in progress */ +#define CIFS_INODE_PENDING_WRITERS	  (1) /* Writes in progress */ +#define CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2 (2) /* Downgrade oplock to L2 */ +#define CIFS_INO_DELETE_PENDING		  (3) /* delete pending on server */ +#define CIFS_INO_INVALID_MAPPING	  (4) /* pagecache is invalid */ +#define CIFS_INO_LOCK			  (5) /* lock bit for synchronization */ +	unsigned long flags; +	spinlock_t writers_lock; +	unsigned int writers;		/* Number of writers on this inode */  	unsigned long time;		/* jiffies of last update of inode */  	u64  server_eof;		/* current file size on server -- protected by i_lock */  	u64  uniqueid;			/* server inode number */ @@ -1272,6 +1329,7 @@ struct dfs_info3_param {  #define CIFS_FATTR_DELETE_PENDING	0x2  #define CIFS_FATTR_NEED_REVAL		0x4  #define CIFS_FATTR_INO_COLLISION	0x8 +#define CIFS_FATTR_UNKNOWN_NLINK	0x10  struct cifs_fattr {  	u32		cf_flags; diff --git a/fs/cifs/cifspdu.h b/fs/cifs/cifspdu.h index 948676db8e2..33df36ef9d5 100644 --- a/fs/cifs/cifspdu.h +++ b/fs/cifs/cifspdu.h @@ -428,7 +428,7 @@ struct smb_hdr {  	__u16 Tid;  	__le16 Pid;  	__u16 Uid; -	__u16 Mid; +	__le16 Mid;  	__u8 WordCount;  } __attribute__((packed)); @@ -697,7 +697,13 @@ struct ntlmssp2_name {  } __attribute__((packed));  struct ntlmv2_resp { -	char ntlmv2_hash[CIFS_ENCPWD_SIZE]; +	union { +	    char ntlmv2_hash[CIFS_ENCPWD_SIZE]; +	    struct { +		__u8 reserved[8]; +		__u8 key[CIFS_SERVER_CHALLENGE_SIZE]; +	    } __attribute__((packed)) challenge; +	} __attribute__((packed));  	__le32 blob_signature;  	__u32  reserved;  	__le64  time; @@ -1352,6 +1358,35 @@ typedef struct smb_com_transaction_ioctl_req {  	__u8 Data[1];  } __attribute__((packed)) TRANSACT_IOCTL_REQ; +typedef struct smb_com_transaction_compr_ioctl_req { +	struct smb_hdr hdr;	/* wct = 23 */ +	__u8 MaxSetupCount; +	__u16 Reserved; +	__le32 TotalParameterCount; +	__le32 TotalDataCount; +	__le32 MaxParameterCount; +	__le32 MaxDataCount; +	__le32 ParameterCount; +	__le32 ParameterOffset; +	__le32 DataCount; +	__le32 DataOffset; +	__u8 SetupCount; /* four setup words follow subcommand */ +	/* SNIA spec incorrectly included spurious pad here */ +	__le16 SubCommand; /* 2 = IOCTL/FSCTL */ +	__le32 FunctionCode; +	__u16 Fid; +	__u8 IsFsctl;  /* 1 = File System Control 0 = device control (IOCTL) */ +	__u8 IsRootFlag; /* 1 = apply command to root of share (must be DFS) */ +	__le16 ByteCount; +	__u8 Pad[3]; +	__le16 compression_state;  /* See below for valid flags */ +} __attribute__((packed)) TRANSACT_COMPR_IOCTL_REQ; + +/* compression state flags */ +#define COMPRESSION_FORMAT_NONE		0x0000 +#define COMPRESSION_FORMAT_DEFAULT	0x0001 +#define COMPRESSION_FORMAT_LZNT1	0x0002 +  typedef struct smb_com_transaction_ioctl_rsp {  	struct smb_hdr hdr;	/* wct = 19 */  	__u8 Reserved[3]; @@ -1491,15 +1526,30 @@ struct file_notify_information {  	__u8  FileName[0];  } __attribute__((packed)); -struct reparse_data { -	__u32	ReparseTag; -	__u16	ReparseDataLength; +/* For IO_REPARSE_TAG_SYMLINK */ +struct reparse_symlink_data { +	__le32	ReparseTag; +	__le16	ReparseDataLength;  	__u16	Reserved; -	__u16	SubstituteNameOffset; -	__u16	SubstituteNameLength; -	__u16	PrintNameOffset; -	__u16	PrintNameLength; -	__u32	Flags; +	__le16	SubstituteNameOffset; +	__le16	SubstituteNameLength; +	__le16	PrintNameOffset; +	__le16	PrintNameLength; +	__le32	Flags; +	char	PathBuffer[0]; +} __attribute__((packed)); + +/* For IO_REPARSE_TAG_NFS */ +#define NFS_SPECFILE_LNK	0x00000000014B4E4C +#define NFS_SPECFILE_CHR	0x0000000000524843 +#define NFS_SPECFILE_BLK	0x00000000004B4C42 +#define NFS_SPECFILE_FIFO	0x000000004F464946 +#define NFS_SPECFILE_SOCK	0x000000004B434F53 +struct reparse_posix_data { +	__le32	ReparseTag; +	__le16	ReparseDataLength; +	__u16	Reserved; +	__le64	InodeType; /* LNK, FIFO, CHR etc. */  	char	PathBuffer[0];  } __attribute__((packed)); @@ -2200,6 +2250,9 @@ typedef struct {  	__le32 DeviceCharacteristics;  } __attribute__((packed)) FILE_SYSTEM_DEVICE_INFO; /* device info level 0x104 */ +/* minimum includes first three fields, and empty FS Name */ +#define MIN_FS_ATTR_INFO_SIZE 12 +  typedef struct {  	__le32 Attributes;  	__le32 MaxPathNameComponentLength; @@ -2652,26 +2705,7 @@ typedef struct file_xattr_info {  } __attribute__((packed)) FILE_XATTR_INFO; /* extended attribute info  					      level 0x205 */ - -/* flags for chattr command */ -#define EXT_SECURE_DELETE		0x00000001 /* EXT3_SECRM_FL */ -#define EXT_ENABLE_UNDELETE		0x00000002 /* EXT3_UNRM_FL */ -/* Reserved for compress file 0x4 */ -#define EXT_SYNCHRONOUS			0x00000008 /* EXT3_SYNC_FL */ -#define EXT_IMMUTABLE_FL		0x00000010 /* EXT3_IMMUTABLE_FL */ -#define EXT_OPEN_APPEND_ONLY		0x00000020 /* EXT3_APPEND_FL */ -#define EXT_DO_NOT_BACKUP		0x00000040 /* EXT3_NODUMP_FL */ -#define EXT_NO_UPDATE_ATIME		0x00000080 /* EXT3_NOATIME_FL */ -/* 0x100 through 0x800 reserved for compression flags and are GET-ONLY */ -#define EXT_HASH_TREE_INDEXED_DIR	0x00001000 /* GET-ONLY EXT3_INDEX_FL */ -/* 0x2000 reserved for IMAGIC_FL */ -#define EXT_JOURNAL_THIS_FILE	0x00004000 /* GET-ONLY EXT3_JOURNAL_DATA_FL */ -/* 0x8000 reserved for EXT3_NOTAIL_FL */ -#define EXT_SYNCHRONOUS_DIR		0x00010000 /* EXT3_DIRSYNC_FL */ -#define EXT_TOPDIR			0x00020000 /* EXT3_TOPDIR_FL */ - -#define EXT_SET_MASK			0x000300FF -#define EXT_GET_MASK			0x0003DFFF +/* flags for lsattr and chflags commands removed arein uapi/linux/fs.h */  typedef struct file_chattr_info {  	__le64	mask; /* list of all possible attribute bits */ diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index b5ec2a268f5..ca7980a1e30 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -127,6 +127,9 @@ extern u64 cifs_UnixTimeToNT(struct timespec);  extern struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time,  				      int offset);  extern void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); +extern int cifs_get_writer(struct cifsInodeInfo *cinode); +extern void cifs_put_writer(struct cifsInodeInfo *cinode); +extern void cifs_done_oplock_break(struct cifsInodeInfo *cinode);  extern int cifs_unlock_range(struct cifsFileInfo *cfile,  			     struct file_lock *flock, const unsigned int xid);  extern int cifs_push_mandatory_locks(struct cifsFileInfo *cfile); @@ -151,7 +154,7 @@ extern struct inode *cifs_iget(struct super_block *sb,  extern int cifs_get_inode_info(struct inode **inode, const char *full_path,  			       FILE_ALL_INFO *data, struct super_block *sb, -			       int xid, const __u16 *fid); +			       int xid, const struct cifs_fid *fid);  extern int cifs_get_inode_info_unix(struct inode **pinode,  			const unsigned char *search_path,  			struct super_block *sb, unsigned int xid); @@ -162,11 +165,13 @@ extern int cifs_rename_pending_delete(const char *full_path,  				      const unsigned int xid);  extern int cifs_acl_to_fattr(struct cifs_sb_info *cifs_sb,  			      struct cifs_fattr *fattr, struct inode *inode, -			      const char *path, const __u16 *pfid); +			      const char *path, const struct cifs_fid *pfid);  extern int id_mode_to_cifs_acl(struct inode *inode, const char *path, __u64,  					kuid_t, kgid_t);  extern struct cifs_ntsd *get_cifs_acl(struct cifs_sb_info *, struct inode *,  					const char *, u32 *); +extern struct cifs_ntsd *get_cifs_acl_by_fid(struct cifs_sb_info *, +						const struct cifs_fid *, u32 *);  extern int set_cifs_acl(struct cifs_ntsd *, __u32, struct inode *,  				const char *, int); @@ -360,11 +365,10 @@ extern int CIFSSMBUnixQuerySymLink(const unsigned int xid,  extern int CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  			       __u16 fid, char **symlinkinfo,  			       const struct nls_table *nls_codepage); -extern int CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, -			const char *fileName, const int disposition, -			const int access_flags, const int omode, -			__u16 *netfid, int *pOplock, FILE_ALL_INFO *, -			const struct nls_table *nls_codepage, int remap); +extern int CIFSSMB_set_compression(const unsigned int xid, +				   struct cifs_tcon *tcon, __u16 fid); +extern int CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, +		     int *oplock, FILE_ALL_INFO *buf);  extern int SMBLegacyOpen(const unsigned int xid, struct cifs_tcon *tcon,  			const char *fileName, const int disposition,  			const int access_flags, const int omode, @@ -474,10 +478,11 @@ extern int CIFSSMBSetPosixACL(const unsigned int xid, struct cifs_tcon *tcon,  extern int CIFSGetExtAttr(const unsigned int xid, struct cifs_tcon *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, unsigned int xid); +extern bool couldbe_mf_symlink(const struct cifs_fattr *fattr); +extern int check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			      struct cifs_sb_info *cifs_sb, +			      struct cifs_fattr *fattr, +			      const unsigned char *path);  extern int mdfour(unsigned char *, unsigned char *, int);  extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,  			const struct nls_table *codepage); @@ -488,12 +493,18 @@ void cifs_readdata_release(struct kref *refcount);  int cifs_async_readv(struct cifs_readdata *rdata);  int cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid); -int cifs_async_writev(struct cifs_writedata *wdata); +int cifs_async_writev(struct cifs_writedata *wdata, +		      void (*release)(struct kref *kref));  void cifs_writev_complete(struct work_struct *work);  struct cifs_writedata *cifs_writedata_alloc(unsigned int nr_pages,  						work_func_t complete);  void cifs_writedata_release(struct kref *refcount); -int open_query_close_cifs_symlink(const unsigned char *path, char *pbuf, -			unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, -			unsigned int xid); +int cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			  struct cifs_sb_info *cifs_sb, +			  const unsigned char *path, char *pbuf, +			  unsigned int *pbytes_read); +int cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +			   struct cifs_sb_info *cifs_sb, +			   const unsigned char *path, char *pbuf, +			   unsigned int *pbytes_written);  #endif			/* _CIFSPROTO_H */ diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index a3d74fea162..6ce4e0954b9 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -463,7 +463,6 @@ decode_lanman_negprot_rsp(struct TCP_Server_Info *server, NEGOTIATE_RSP *pSMBr)  			       cifs_max_pending);  	set_credits(server, server->maxReq);  	server->maxBuf = le16_to_cpu(rsp->MaxBufSize); -	server->max_vcs = le16_to_cpu(rsp->MaxNumberVcs);  	/* even though we do not use raw we might as well set this  	accurately, in case we ever find a need for it */  	if ((le16_to_cpu(rsp->RawMode) & RAW_ENABLE) == RAW_ENABLE) { @@ -1274,104 +1273,124 @@ OldOpenRetry:  }  int -CIFSSMBOpen(const unsigned int xid, struct cifs_tcon *tcon, -	    const char *fileName, const int openDisposition, -	    const int access_flags, const int create_options, __u16 *netfid, -	    int *pOplock, FILE_ALL_INFO *pfile_info, -	    const struct nls_table *nls_codepage, int remap) +CIFS_open(const unsigned int xid, struct cifs_open_parms *oparms, int *oplock, +	  FILE_ALL_INFO *buf)  {  	int rc = -EACCES; -	OPEN_REQ *pSMB = NULL; -	OPEN_RSP *pSMBr = NULL; +	OPEN_REQ *req = NULL; +	OPEN_RSP *rsp = NULL;  	int bytes_returned;  	int name_len;  	__u16 count; +	struct cifs_sb_info *cifs_sb = oparms->cifs_sb; +	struct cifs_tcon *tcon = oparms->tcon; +	int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; +	const struct nls_table *nls = cifs_sb->local_nls; +	int create_options = oparms->create_options; +	int desired_access = oparms->desired_access; +	int disposition = oparms->disposition; +	const char *path = oparms->path;  openRetry: -	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **) &pSMB, -		      (void **) &pSMBr); +	rc = smb_init(SMB_COM_NT_CREATE_ANDX, 24, tcon, (void **)&req, +		      (void **)&rsp);  	if (rc)  		return rc; -	pSMB->AndXCommand = 0xFF;	/* none */ +	/* no commands go after this */ +	req->AndXCommand = 0xFF; -	if (pSMB->hdr.Flags2 & SMBFLG2_UNICODE) { -		count = 1;	/* account for one byte pad to word boundary */ -		name_len = -		    cifsConvertToUTF16((__le16 *) (pSMB->fileName + 1), -				       fileName, PATH_MAX, nls_codepage, remap); -		name_len++;	/* trailing null */ +	if (req->hdr.Flags2 & SMBFLG2_UNICODE) { +		/* account for one byte pad to word boundary */ +		count = 1; +		name_len = cifsConvertToUTF16((__le16 *)(req->fileName + 1), +					      path, PATH_MAX, nls, remap); +		/* trailing null */ +		name_len++;  		name_len *= 2; -		pSMB->NameLength = cpu_to_le16(name_len); -	} else {		/* BB improve check for buffer overruns BB */ -		count = 0;	/* no pad */ -		name_len = strnlen(fileName, PATH_MAX); -		name_len++;	/* trailing null */ -		pSMB->NameLength = cpu_to_le16(name_len); -		strncpy(pSMB->fileName, fileName, name_len); +		req->NameLength = cpu_to_le16(name_len); +	} else { +		/* BB improve check for buffer overruns BB */ +		/* no pad */ +		count = 0; +		name_len = strnlen(path, PATH_MAX); +		/* trailing null */ +		name_len++; +		req->NameLength = cpu_to_le16(name_len); +		strncpy(req->fileName, path, name_len);  	} -	if (*pOplock & REQ_OPLOCK) -		pSMB->OpenFlags = cpu_to_le32(REQ_OPLOCK); -	else if (*pOplock & REQ_BATCHOPLOCK) -		pSMB->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); -	pSMB->DesiredAccess = cpu_to_le32(access_flags); -	pSMB->AllocationSize = 0; -	/* set file as system file if special file such -	   as fifo and server expecting SFU style and -	   no Unix extensions */ + +	if (*oplock & REQ_OPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_OPLOCK); +	else if (*oplock & REQ_BATCHOPLOCK) +		req->OpenFlags = cpu_to_le32(REQ_BATCHOPLOCK); + +	req->DesiredAccess = cpu_to_le32(desired_access); +	req->AllocationSize = 0; + +	/* +	 * Set file as system file if special file such as fifo and server +	 * expecting SFU style and no Unix extensions. +	 */  	if (create_options & CREATE_OPTION_SPECIAL) -		pSMB->FileAttributes = cpu_to_le32(ATTR_SYSTEM); +		req->FileAttributes = cpu_to_le32(ATTR_SYSTEM);  	else -		pSMB->FileAttributes = cpu_to_le32(ATTR_NORMAL); +		req->FileAttributes = cpu_to_le32(ATTR_NORMAL); -	/* XP does not handle ATTR_POSIX_SEMANTICS */ -	/* but it helps speed up case sensitive checks for other -	servers such as Samba */ +	/* +	 * XP does not handle ATTR_POSIX_SEMANTICS but it helps speed up case +	 * sensitive checks for other servers such as Samba. +	 */  	if (tcon->ses->capabilities & CAP_UNIX) -		pSMB->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS); +		req->FileAttributes |= cpu_to_le32(ATTR_POSIX_SEMANTICS);  	if (create_options & CREATE_OPTION_READONLY) -		pSMB->FileAttributes |= cpu_to_le32(ATTR_READONLY); +		req->FileAttributes |= cpu_to_le32(ATTR_READONLY); + +	req->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); +	req->CreateDisposition = cpu_to_le32(disposition); +	req->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK); -	pSMB->ShareAccess = cpu_to_le32(FILE_SHARE_ALL); -	pSMB->CreateDisposition = cpu_to_le32(openDisposition); -	pSMB->CreateOptions = cpu_to_le32(create_options & CREATE_OPTIONS_MASK);  	/* BB Expirement with various impersonation levels and verify */ -	pSMB->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); -	pSMB->SecurityFlags = -	    SECURITY_CONTEXT_TRACKING | SECURITY_EFFECTIVE_ONLY; +	req->ImpersonationLevel = cpu_to_le32(SECURITY_IMPERSONATION); +	req->SecurityFlags = SECURITY_CONTEXT_TRACKING|SECURITY_EFFECTIVE_ONLY;  	count += name_len; -	inc_rfc1001_len(pSMB, count); +	inc_rfc1001_len(req, count); -	pSMB->ByteCount = cpu_to_le16(count); -	/* long_op set to 1 to allow for oplock break timeouts */ -	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, -			(struct smb_hdr *)pSMBr, &bytes_returned, 0); +	req->ByteCount = cpu_to_le16(count); +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *)req, +			 (struct smb_hdr *)rsp, &bytes_returned, 0);  	cifs_stats_inc(&tcon->stats.cifs_stats.num_opens);  	if (rc) {  		cifs_dbg(FYI, "Error in Open = %d\n", rc); -	} else { -		*pOplock = pSMBr->OplockLevel; /* 1 byte no need to le_to_cpu */ -		*netfid = pSMBr->Fid;	/* cifs fid stays in le */ -		/* Let caller know file was created so we can set the mode. */ -		/* Do we care about the CreateAction in any other cases? */ -		if (cpu_to_le32(FILE_CREATE) == pSMBr->CreateAction) -			*pOplock |= CIFS_CREATE_ACTION; -		if (pfile_info) { -			memcpy((char *)pfile_info, (char *)&pSMBr->CreationTime, -				36 /* CreationTime to Attributes */); -			/* the file_info buf is endian converted by caller */ -			pfile_info->AllocationSize = pSMBr->AllocationSize; -			pfile_info->EndOfFile = pSMBr->EndOfFile; -			pfile_info->NumberOfLinks = cpu_to_le32(1); -			pfile_info->DeletePending = 0; -		} +		cifs_buf_release(req); +		if (rc == -EAGAIN) +			goto openRetry; +		return rc;  	} -	cifs_buf_release(pSMB); -	if (rc == -EAGAIN) -		goto openRetry; +	/* 1 byte no need to le_to_cpu */ +	*oplock = rsp->OplockLevel; +	/* cifs fid stays in le */ +	oparms->fid->netfid = rsp->Fid; + +	/* Let caller know file was created so we can set the mode. */ +	/* Do we care about the CreateAction in any other cases? */ +	if (cpu_to_le32(FILE_CREATE) == rsp->CreateAction) +		*oplock |= CIFS_CREATE_ACTION; + +	if (buf) { +		/* copy from CreationTime to Attributes */ +		memcpy((char *)buf, (char *)&rsp->CreationTime, 36); +		/* the file_info buf is endian converted by caller */ +		buf->AllocationSize = rsp->AllocationSize; +		buf->EndOfFile = rsp->EndOfFile; +		buf->NumberOfLinks = cpu_to_le32(1); +		buf->DeletePending = 0; +	} + +	cifs_buf_release(req);  	return rc;  } @@ -1891,7 +1910,7 @@ cifs_writev_requeue(struct cifs_writedata *wdata)  	do {  		server = tlink_tcon(wdata->cfile->tlink)->ses->server; -		rc = server->ops->async_writev(wdata); +		rc = server->ops->async_writev(wdata, cifs_writedata_release);  	} while (rc == -EAGAIN);  	for (i = 0; i < wdata->nr_pages; i++) { @@ -1943,15 +1962,9 @@ cifs_writedata_alloc(unsigned int nr_pages, work_func_t complete)  {  	struct cifs_writedata *wdata; -	/* this would overflow */ -	if (nr_pages == 0) { -		cifs_dbg(VFS, "%s: called with nr_pages == 0!\n", __func__); -		return NULL; -	} -  	/* writedata + number of page pointers */  	wdata = kzalloc(sizeof(*wdata) + -			sizeof(struct page *) * (nr_pages - 1), GFP_NOFS); +			sizeof(struct page *) * nr_pages, GFP_NOFS);  	if (wdata != NULL) {  		kref_init(&wdata->refcount);  		INIT_LIST_HEAD(&wdata->list); @@ -2012,7 +2025,8 @@ cifs_writev_callback(struct mid_q_entry *mid)  /* cifs_async_writev - send an async write, and set up mid to handle result */  int -cifs_async_writev(struct cifs_writedata *wdata) +cifs_async_writev(struct cifs_writedata *wdata, +		  void (*release)(struct kref *kref))  {  	int rc = -EACCES;  	WRITE_REQ *smb = NULL; @@ -2086,7 +2100,7 @@ cifs_async_writev(struct cifs_writedata *wdata)  	if (rc == 0)  		cifs_stats_inc(&tcon->stats.cifs_stats.num_writes);  	else -		kref_put(&wdata->refcount, cifs_writedata_release); +		kref_put(&wdata->refcount, release);  async_writev_out:  	cifs_small_buf_release(smb); @@ -3089,7 +3103,8 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  	bool is_unicode;  	unsigned int sub_len;  	char *sub_start; -	struct reparse_data *reparse_buf; +	struct reparse_symlink_data *reparse_buf; +	struct reparse_posix_data *posix_buf;  	__u32 data_offset, data_count;  	char *end_of_smb; @@ -3138,20 +3153,47 @@ CIFSSMBQuerySymLink(const unsigned int xid, struct cifs_tcon *tcon,  		goto qreparse_out;  	}  	end_of_smb = 2 + get_bcc(&pSMBr->hdr) + (char *)&pSMBr->ByteCount; -	reparse_buf = (struct reparse_data *) +	reparse_buf = (struct reparse_symlink_data *)  				((char *)&pSMBr->hdr.Protocol + data_offset);  	if ((char *)reparse_buf >= end_of_smb) {  		rc = -EIO;  		goto qreparse_out;  	} -	if ((reparse_buf->PathBuffer + reparse_buf->PrintNameOffset + -				reparse_buf->PrintNameLength) > end_of_smb) { +	if (reparse_buf->ReparseTag == cpu_to_le32(IO_REPARSE_TAG_NFS)) { +		cifs_dbg(FYI, "NFS style reparse tag\n"); +		posix_buf =  (struct reparse_posix_data *)reparse_buf; + +		if (posix_buf->InodeType != cpu_to_le64(NFS_SPECFILE_LNK)) { +			cifs_dbg(FYI, "unsupported file type 0x%llx\n", +				 le64_to_cpu(posix_buf->InodeType)); +			rc = -EOPNOTSUPP; +			goto qreparse_out; +		} +		is_unicode = true; +		sub_len = le16_to_cpu(reparse_buf->ReparseDataLength); +		if (posix_buf->PathBuffer + sub_len > end_of_smb) { +			cifs_dbg(FYI, "reparse buf beyond SMB\n"); +			rc = -EIO; +			goto qreparse_out; +		} +		*symlinkinfo = cifs_strndup_from_utf16(posix_buf->PathBuffer, +				sub_len, is_unicode, nls_codepage); +		goto qreparse_out; +	} else if (reparse_buf->ReparseTag != +			cpu_to_le32(IO_REPARSE_TAG_SYMLINK)) { +		rc = -EOPNOTSUPP; +		goto qreparse_out; +	} + +	/* Reparse tag is NTFS symlink */ +	sub_start = le16_to_cpu(reparse_buf->SubstituteNameOffset) + +				reparse_buf->PathBuffer; +	sub_len = le16_to_cpu(reparse_buf->SubstituteNameLength); +	if (sub_start + sub_len > end_of_smb) {  		cifs_dbg(FYI, "reparse buf beyond SMB\n");  		rc = -EIO;  		goto qreparse_out;  	} -	sub_start = reparse_buf->SubstituteNameOffset + reparse_buf->PathBuffer; -	sub_len = reparse_buf->SubstituteNameLength;  	if (pSMBr->hdr.Flags2 & SMBFLG2_UNICODE)  		is_unicode = true;  	else @@ -3172,6 +3214,60 @@ qreparse_out:  	return rc;  } +int +CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		    __u16 fid) +{ +	int rc = 0; +	int bytes_returned; +	struct smb_com_transaction_compr_ioctl_req *pSMB; +	struct smb_com_transaction_ioctl_rsp *pSMBr; + +	cifs_dbg(FYI, "Set compression for %u\n", fid); +	rc = smb_init(SMB_COM_NT_TRANSACT, 23, tcon, (void **) &pSMB, +		      (void **) &pSMBr); +	if (rc) +		return rc; + +	pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + +	pSMB->TotalParameterCount = 0; +	pSMB->TotalDataCount = __constant_cpu_to_le32(2); +	pSMB->MaxParameterCount = 0; +	pSMB->MaxDataCount = 0; +	pSMB->MaxSetupCount = 4; +	pSMB->Reserved = 0; +	pSMB->ParameterOffset = 0; +	pSMB->DataCount = __constant_cpu_to_le32(2); +	pSMB->DataOffset = +		cpu_to_le32(offsetof(struct smb_com_transaction_compr_ioctl_req, +				compression_state) - 4);  /* 84 */ +	pSMB->SetupCount = 4; +	pSMB->SubCommand = __constant_cpu_to_le16(NT_TRANSACT_IOCTL); +	pSMB->ParameterCount = 0; +	pSMB->FunctionCode = __constant_cpu_to_le32(FSCTL_SET_COMPRESSION); +	pSMB->IsFsctl = 1; /* FSCTL */ +	pSMB->IsRootFlag = 0; +	pSMB->Fid = fid; /* file handle always le */ +	/* 3 byte pad, followed by 2 byte compress state */ +	pSMB->ByteCount = __constant_cpu_to_le16(5); +	inc_rfc1001_len(pSMB, 5); + +	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, +			 (struct smb_hdr *) pSMBr, &bytes_returned, 0); +	if (rc) +		cifs_dbg(FYI, "Send error in SetCompression = %d\n", rc); + +	cifs_buf_release(pSMB); + +	/* +	 * Note: On -EAGAIN error only caller can retry on handle based calls +	 * since file handle passed in no longer valid. +	 */ +	return rc; +} + +  #ifdef CONFIG_CIFS_POSIX  /*Convert an Access Control Entry from wire format to local POSIX xattr format*/ @@ -3288,11 +3384,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,  		return 0;  	}  	cifs_acl->version = cpu_to_le16(1); -	if (acl_type == ACL_TYPE_ACCESS) +	if (acl_type == ACL_TYPE_ACCESS) {  		cifs_acl->access_entry_count = cpu_to_le16(count); -	else if (acl_type == ACL_TYPE_DEFAULT) +		cifs_acl->default_entry_count = __constant_cpu_to_le16(0xFFFF); +	} else if (acl_type == ACL_TYPE_DEFAULT) {  		cifs_acl->default_entry_count = cpu_to_le16(count); -	else { +		cifs_acl->access_entry_count = __constant_cpu_to_le16(0xFFFF); +	} else {  		cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);  		return 0;  	} @@ -3927,7 +4025,7 @@ QFileInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in QFileInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -4096,7 +4194,7 @@ UnixQFileInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in UnixQFileInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -4180,7 +4278,7 @@ UnixQPathInfoRetry:  	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,  			 (struct smb_hdr *) pSMBr, &bytes_returned, 0);  	if (rc) { -		cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); +		cifs_dbg(FYI, "Send error in UnixQPathInfo = %d", rc);  	} else {		/* decode response */  		rc = validate_t2((struct smb_t2_rsp *)pSMBr); @@ -6099,6 +6197,9 @@ QAllEAsRetry:  	cifs_dbg(FYI, "ea length %d\n", list_len);  	if (list_len <= 8) {  		cifs_dbg(FYI, "empty EA list returned from server\n"); +		/* didn't find the named attribute */ +		if (ea_name) +			rc = -ENODATA;  		goto QAllEAsOut;  	} diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index a279ffc0bc2..20d75b8ddb2 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2144,6 +2144,9 @@ cifs_get_tcp_session(struct smb_vol *volume_info)  	       sizeof(tcp_ses->srcaddr));  	memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,  		sizeof(tcp_ses->dstaddr)); +#ifdef CONFIG_CIFS_SMB2 +	get_random_bytes(tcp_ses->client_guid, SMB2_CLIENT_GUID_SIZE); +#endif  	/*  	 * at this point we are the only ones with the pointer  	 * to the struct since the kernel thread not created yet @@ -2225,7 +2228,7 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)  			    vol->username ? vol->username : "",  			    CIFS_MAX_USERNAME_LEN))  			return 0; -		if (strlen(vol->username) != 0 && +		if ((vol->username && strlen(vol->username) != 0) &&  		    ses->password != NULL &&  		    strncmp(ses->password,  			    vol->password ? vol->password : "", @@ -2242,6 +2245,8 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)  	spin_lock(&cifs_tcp_ses_lock);  	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { +		if (ses->status == CifsExiting) +			continue;  		if (!match_session(ses, vol))  			continue;  		++ses->ses_count; @@ -2255,24 +2260,37 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)  static void  cifs_put_smb_ses(struct cifs_ses *ses)  { -	unsigned int xid; +	unsigned int rc, xid;  	struct TCP_Server_Info *server = ses->server;  	cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count); +  	spin_lock(&cifs_tcp_ses_lock); +	if (ses->status == CifsExiting) { +		spin_unlock(&cifs_tcp_ses_lock); +		return; +	}  	if (--ses->ses_count > 0) {  		spin_unlock(&cifs_tcp_ses_lock);  		return;  	} - -	list_del_init(&ses->smb_ses_list); +	if (ses->status == CifsGood) +		ses->status = CifsExiting;  	spin_unlock(&cifs_tcp_ses_lock); -	if (ses->status == CifsGood && server->ops->logoff) { +	if (ses->status == CifsExiting && server->ops->logoff) {  		xid = get_xid(); -		server->ops->logoff(xid, ses); +		rc = server->ops->logoff(xid, ses); +		if (rc) +			cifs_dbg(VFS, "%s: Session Logoff failure rc=%d\n", +				__func__, rc);  		_free_xid(xid);  	} + +	spin_lock(&cifs_tcp_ses_lock); +	list_del_init(&ses->smb_ses_list); +	spin_unlock(&cifs_tcp_ses_lock); +  	sesInfoFree(ses);  	cifs_put_tcp_session(server);  } @@ -3755,6 +3773,13 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,  	return rc;  } +static void delayed_free(struct rcu_head *p) +{ +	struct cifs_sb_info *sbi = container_of(p, struct cifs_sb_info, rcu); +	unload_nls(sbi->local_nls); +	kfree(sbi); +} +  void  cifs_umount(struct cifs_sb_info *cifs_sb)  { @@ -3779,8 +3804,7 @@ cifs_umount(struct cifs_sb_info *cifs_sb)  	bdi_destroy(&cifs_sb->bdi);  	kfree(cifs_sb->mountdata); -	unload_nls(cifs_sb->local_nls); -	kfree(cifs_sb); +	call_rcu(&cifs_sb->rcu, delayed_free);  }  int diff --git a/fs/cifs/dir.c b/fs/cifs/dir.c index d3e2eaa503a..3db0c5fd9a1 100644 --- a/fs/cifs/dir.c +++ b/fs/cifs/dir.c @@ -193,7 +193,7 @@ check_name(struct dentry *direntry)  static int  cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,  	       struct tcon_link *tlink, unsigned oflags, umode_t mode, -	       __u32 *oplock, struct cifs_fid *fid, int *created) +	       __u32 *oplock, struct cifs_fid *fid)  {  	int rc = -ENOENT;  	int create_options = CREATE_NOT_DIR; @@ -349,7 +349,6 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,  				.device	= 0,  		}; -		*created |= FILE_CREATED;  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SET_UID) {  			args.uid = current_fsuid();  			if (inode->i_mode & S_ISGID) @@ -379,7 +378,7 @@ cifs_create_get_file_info:  					      xid);  	else {  		rc = cifs_get_inode_info(&newinode, full_path, buf, inode->i_sb, -					 xid, &fid->netfid); +					 xid, fid);  		if (newinode) {  			if (server->ops->set_lease_key)  				server->ops->set_lease_key(newinode, fid); @@ -480,13 +479,16 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,  	cifs_add_pending_open(&fid, tlink, &open);  	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, -			    &oplock, &fid, opened); +			    &oplock, &fid);  	if (rc) {  		cifs_del_pending_open(&open);  		goto out;  	} +	if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) +		*opened |= FILE_CREATED; +  	rc = finish_open(file, direntry, generic_file_open, opened);  	if (rc) {  		if (server->ops->close) @@ -500,6 +502,7 @@ cifs_atomic_open(struct inode *inode, struct dentry *direntry,  		if (server->ops->close)  			server->ops->close(xid, tcon, &fid);  		cifs_del_pending_open(&open); +		fput(file);  		rc = -ENOMEM;  	} @@ -528,7 +531,6 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,  	struct TCP_Server_Info *server;  	struct cifs_fid fid;  	__u32 oplock; -	int created = FILE_CREATED;  	cifs_dbg(FYI, "cifs_create parent inode = 0x%p name is: %s and dentry = 0x%p\n",  		 inode, direntry->d_name.name, direntry); @@ -545,7 +547,7 @@ int cifs_create(struct inode *inode, struct dentry *direntry, umode_t mode,  		server->ops->new_lease_key(&fid);  	rc = cifs_do_create(inode, direntry, xid, tlink, oflags, mode, -			    &oplock, &fid, &created); +			    &oplock, &fid);  	if (!rc && server->ops->close)  		server->ops->close(xid, tcon, &fid); @@ -563,12 +565,13 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  	int create_options = CREATE_NOT_DIR | CREATE_OPTION_SPECIAL;  	struct cifs_sb_info *cifs_sb;  	struct tcon_link *tlink; -	struct cifs_tcon *pTcon; +	struct cifs_tcon *tcon;  	struct cifs_io_parms io_parms;  	char *full_path = NULL;  	struct inode *newinode = NULL;  	int oplock = 0; -	u16 fileHandle; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	FILE_ALL_INFO *buf = NULL;  	unsigned int bytes_written;  	struct win_dev *pdev; @@ -581,7 +584,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  	if (IS_ERR(tlink))  		return PTR_ERR(tlink); -	pTcon = tlink_tcon(tlink); +	tcon = tlink_tcon(tlink);  	xid = get_xid(); @@ -591,7 +594,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  		goto mknod_out;  	} -	if (pTcon->unix_ext) { +	if (tcon->unix_ext) {  		struct cifs_unix_set_info_args args = {  			.mode	= mode & ~current_umask(),  			.ctime	= NO_CHANGE_64, @@ -606,7 +609,7 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  			args.uid = INVALID_UID; /* no change */  			args.gid = INVALID_GID; /* no change */  		} -		rc = CIFSSMBUnixSetPathInfo(xid, pTcon, full_path, &args, +		rc = CIFSSMBUnixSetPathInfo(xid, tcon, full_path, &args,  					    cifs_sb->local_nls,  					    cifs_sb->mnt_cifs_flags &  						CIFS_MOUNT_MAP_SPECIAL_CHR); @@ -638,42 +641,44 @@ int cifs_mknod(struct inode *inode, struct dentry *direntry, umode_t mode,  	if (backup_cred(cifs_sb))  		create_options |= CREATE_OPEN_BACKUP_INTENT; -	rc = CIFSSMBOpen(xid, pTcon, full_path, FILE_CREATE, -			 GENERIC_WRITE, create_options, -			 &fileHandle, &oplock, buf, cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_WRITE; +	oparms.create_options = create_options; +	oparms.disposition = FILE_CREATE; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, buf);  	if (rc)  		goto mknod_out; -	/* BB Do not bother to decode buf since no local inode yet to put -	 * timestamps in, but we can reuse it safely */ +	/* +	 * BB Do not bother to decode buf since no local inode yet to put +	 * timestamps in, but we can reuse it safely. +	 */  	pdev = (struct win_dev *)buf; -	io_parms.netfid = fileHandle; +	io_parms.netfid = fid.netfid;  	io_parms.pid = current->tgid; -	io_parms.tcon = pTcon; +	io_parms.tcon = tcon;  	io_parms.offset = 0;  	io_parms.length = sizeof(struct win_dev);  	if (S_ISCHR(mode)) {  		memcpy(pdev->type, "IntxCHR", 8); -		pdev->major = -		      cpu_to_le64(MAJOR(device_number)); -		pdev->minor = -		      cpu_to_le64(MINOR(device_number)); -		rc = CIFSSMBWrite(xid, &io_parms, -			&bytes_written, (char *)pdev, -			NULL, 0); +		pdev->major = cpu_to_le64(MAJOR(device_number)); +		pdev->minor = cpu_to_le64(MINOR(device_number)); +		rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, +				  NULL, 0);  	} else if (S_ISBLK(mode)) {  		memcpy(pdev->type, "IntxBLK", 8); -		pdev->major = -		      cpu_to_le64(MAJOR(device_number)); -		pdev->minor = -		      cpu_to_le64(MINOR(device_number)); -		rc = CIFSSMBWrite(xid, &io_parms, -			&bytes_written, (char *)pdev, -			NULL, 0); +		pdev->major = cpu_to_le64(MAJOR(device_number)); +		pdev->minor = cpu_to_le64(MINOR(device_number)); +		rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, (char *)pdev, +				  NULL, 0);  	} /* else if (S_ISFIFO) */ -	CIFSSMBClose(xid, pTcon, fileHandle); +	CIFSSMBClose(xid, tcon, fid.netfid);  	d_drop(direntry);  	/* FIXME: add code here to set EAs */ @@ -755,7 +760,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry,  	/*	if it was once a directory (but how can we tell?) we could do  		shrink_dcache_parent(direntry); */  	} else if (rc != -EACCES) { -		cifs_dbg(VFS, "Unexpected lookup error %d\n", rc); +		cifs_dbg(FYI, "Unexpected lookup error %d\n", rc);  		/* We special case check for Access Denied - since that  		is a common return code */  	} diff --git a/fs/cifs/file.c b/fs/cifs/file.c index eb955b525e5..e90a1e9aa62 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -244,7 +244,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,  					      xid);  	else  		rc = cifs_get_inode_info(&inode, full_path, buf, inode->i_sb, -					 xid, &fid->netfid); +					 xid, fid);  out:  	kfree(buf); @@ -335,7 +335,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,  	spin_unlock(&cifs_file_list_lock);  	if (fid->purge_cache) -		cifs_invalidate_mapping(inode); +		cifs_zap_mapping(inode);  	file->private_data = cfile;  	return cfile; @@ -392,7 +392,7 @@ void cifsFileInfo_put(struct cifsFileInfo *cifs_file)  		 * again and get at least level II oplock.  		 */  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_STRICT_IO) -			CIFS_I(inode)->invalid_mapping = true; +			set_bit(CIFS_INO_INVALID_MAPPING, &cifsi->flags);  		cifs_set_oplock_level(cifsi, 0);  	}  	spin_unlock(&cifs_file_list_lock); @@ -678,7 +678,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)  	/*  	 * Can not refresh inode by passing in file_info buf to be returned by -	 * CIFSSMBOpen and then calling get_inode_info with returned buf since +	 * ops->open and then calling get_inode_info with returned buf since  	 * file might have write behind data that needs to be flushed and server  	 * version of file size can be stale. If we knew for sure that inode was  	 * not dirty locally we could do this. @@ -1529,7 +1529,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,  		 */  		if (!CIFS_CACHE_WRITE(CIFS_I(inode)) &&  					CIFS_CACHE_READ(CIFS_I(inode))) { -			cifs_invalidate_mapping(inode); +			cifs_zap_mapping(inode);  			cifs_dbg(FYI, "Set no oplock for inode=%p due to mand locks\n",  				 inode);  			CIFS_I(inode)->oplock = 0; @@ -2043,7 +2043,8 @@ retry:  			}  			wdata->pid = wdata->cfile->pid;  			server = tlink_tcon(wdata->cfile->tlink)->ses->server; -			rc = server->ops->async_writev(wdata); +			rc = server->ops->async_writev(wdata, +							cifs_writedata_release);  		} while (wbc->sync_mode == WB_SYNC_ALL && rc == -EAGAIN);  		for (i = 0; i < nr_pages; ++i) @@ -2217,7 +2218,7 @@ int cifs_strict_fsync(struct file *file, loff_t start, loff_t end,  		 file->f_path.dentry->d_name.name, datasync);  	if (!CIFS_CACHE_READ(CIFS_I(inode))) { -		rc = cifs_invalidate_mapping(inode); +		rc = cifs_zap_mapping(inode);  		if (rc) {  			cifs_dbg(FYI, "rc: %d during invalidate phase\n", rc);  			rc = 0; /* don't care about it in fsync */ @@ -2331,9 +2332,20 @@ size_t get_numpages(const size_t wsize, const size_t len, size_t *cur_len)  }  static void -cifs_uncached_writev_complete(struct work_struct *work) +cifs_uncached_writedata_release(struct kref *refcount)  {  	int i; +	struct cifs_writedata *wdata = container_of(refcount, +					struct cifs_writedata, refcount); + +	for (i = 0; i < wdata->nr_pages; i++) +		put_page(wdata->pages[i]); +	cifs_writedata_release(refcount); +} + +static void +cifs_uncached_writev_complete(struct work_struct *work) +{  	struct cifs_writedata *wdata = container_of(work,  					struct cifs_writedata, work);  	struct inode *inode = wdata->cfile->dentry->d_inode; @@ -2347,12 +2359,7 @@ cifs_uncached_writev_complete(struct work_struct *work)  	complete(&wdata->done); -	if (wdata->result != -EAGAIN) { -		for (i = 0; i < wdata->nr_pages; i++) -			put_page(wdata->pages[i]); -	} - -	kref_put(&wdata->refcount, cifs_writedata_release); +	kref_put(&wdata->refcount, cifs_uncached_writedata_release);  }  /* attempt to send write to server, retry on any -EAGAIN errors */ @@ -2370,21 +2377,20 @@ cifs_uncached_retry_writev(struct cifs_writedata *wdata)  			if (rc != 0)  				continue;  		} -		rc = server->ops->async_writev(wdata); +		rc = server->ops->async_writev(wdata, +					       cifs_uncached_writedata_release);  	} while (rc == -EAGAIN);  	return rc;  }  static ssize_t -cifs_iovec_write(struct file *file, const struct iovec *iov, -		 unsigned long nr_segs, loff_t *poffset) +cifs_iovec_write(struct file *file, struct iov_iter *from, loff_t *poffset)  {  	unsigned long nr_pages, i; -	size_t copied, len, cur_len; +	size_t bytes, copied, len, cur_len;  	ssize_t total_written = 0;  	loff_t offset; -	struct iov_iter it;  	struct cifsFileInfo *open_file;  	struct cifs_tcon *tcon;  	struct cifs_sb_info *cifs_sb; @@ -2393,14 +2399,16 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,  	int rc;  	pid_t pid; -	len = iov_length(iov, nr_segs); -	if (!len) -		return 0; - +	len = iov_iter_count(from);  	rc = generic_write_checks(file, poffset, &len, 0);  	if (rc)  		return rc; +	if (!len) +		return 0; + +	iov_iter_truncate(from, len); +  	INIT_LIST_HEAD(&wdata_list);  	cifs_sb = CIFS_SB(file->f_path.dentry->d_sb);  	open_file = file->private_data; @@ -2416,7 +2424,6 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,  	else  		pid = current->tgid; -	iov_iter_init(&it, iov, nr_segs, len, 0);  	do {  		size_t save_len; @@ -2436,14 +2443,44 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,  		save_len = cur_len;  		for (i = 0; i < nr_pages; i++) { -			copied = min_t(const size_t, cur_len, PAGE_SIZE); -			copied = iov_iter_copy_from_user(wdata->pages[i], &it, -							 0, copied); +			bytes = min_t(size_t, cur_len, PAGE_SIZE); +			copied = copy_page_from_iter(wdata->pages[i], 0, bytes, +						     from);  			cur_len -= copied; -			iov_iter_advance(&it, copied); +			/* +			 * If we didn't copy as much as we expected, then that +			 * may mean we trod into an unmapped area. Stop copying +			 * at that point. On the next pass through the big +			 * loop, we'll likely end up getting a zero-length +			 * write and bailing out of it. +			 */ +			if (copied < bytes) +				break;  		}  		cur_len = save_len - cur_len; +		/* +		 * If we have no data to send, then that probably means that +		 * the copy above failed altogether. That's most likely because +		 * the address in the iovec was bogus. Set the rc to -EFAULT, +		 * free anything we allocated and bail out. +		 */ +		if (!cur_len) { +			for (i = 0; i < nr_pages; i++) +				put_page(wdata->pages[i]); +			kfree(wdata); +			rc = -EFAULT; +			break; +		} + +		/* +		 * i + 1 now represents the number of pages we actually used in +		 * the copy phase above. Bring nr_pages down to that, and free +		 * any pages that we didn't use. +		 */ +		for ( ; nr_pages > i + 1; nr_pages--) +			put_page(wdata->pages[nr_pages - 1]); +  		wdata->sync_mode = WB_SYNC_ALL;  		wdata->nr_pages = nr_pages;  		wdata->offset = (__u64)offset; @@ -2454,7 +2491,8 @@ cifs_iovec_write(struct file *file, const struct iovec *iov,  		wdata->tailsz = cur_len - ((nr_pages - 1) * PAGE_SIZE);  		rc = cifs_uncached_retry_writev(wdata);  		if (rc) { -			kref_put(&wdata->refcount, cifs_writedata_release); +			kref_put(&wdata->refcount, +				 cifs_uncached_writedata_release);  			break;  		} @@ -2496,7 +2534,7 @@ restart_loop:  			}  		}  		list_del_init(&wdata->list); -		kref_put(&wdata->refcount, cifs_writedata_release); +		kref_put(&wdata->refcount, cifs_uncached_writedata_release);  	}  	if (total_written > 0) @@ -2506,11 +2544,11 @@ restart_loop:  	return total_written ? total_written : (ssize_t)rc;  } -ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov, -				unsigned long nr_segs, loff_t pos) +ssize_t cifs_user_writev(struct kiocb *iocb, struct iov_iter *from)  {  	ssize_t written;  	struct inode *inode; +	loff_t pos = iocb->ki_pos;  	inode = file_inode(iocb->ki_filp); @@ -2520,9 +2558,9 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,  	 * write request.  	 */ -	written = cifs_iovec_write(iocb->ki_filp, iov, nr_segs, &pos); +	written = cifs_iovec_write(iocb->ki_filp, from, &pos);  	if (written > 0) { -		CIFS_I(inode)->invalid_mapping = true; +		set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags);  		iocb->ki_pos = pos;  	} @@ -2530,8 +2568,7 @@ ssize_t cifs_user_writev(struct kiocb *iocb, const struct iovec *iov,  }  static ssize_t -cifs_writev(struct kiocb *iocb, const struct iovec *iov, -	    unsigned long nr_segs, loff_t pos) +cifs_writev(struct kiocb *iocb, struct iov_iter *from)  {  	struct file *file = iocb->ki_filp;  	struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; @@ -2539,38 +2576,38 @@ cifs_writev(struct kiocb *iocb, const struct iovec *iov,  	struct cifsInodeInfo *cinode = CIFS_I(inode);  	struct TCP_Server_Info *server = tlink_tcon(cfile->tlink)->ses->server;  	ssize_t rc = -EACCES; - -	BUG_ON(iocb->ki_pos != pos); +	loff_t lock_pos = iocb->ki_pos;  	/*  	 * We need to hold the sem to be sure nobody modifies lock list  	 * with a brlock that prevents writing.  	 */  	down_read(&cinode->lock_sem); -	if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs), +	mutex_lock(&inode->i_mutex); +	if (file->f_flags & O_APPEND) +		lock_pos = i_size_read(inode); +	if (!cifs_find_lock_conflict(cfile, lock_pos, iov_iter_count(from),  				     server->vals->exclusive_lock_type, NULL,  				     CIFS_WRITE_OP)) { -		mutex_lock(&inode->i_mutex); -		rc = __generic_file_aio_write(iocb, iov, nr_segs, -					       &iocb->ki_pos); +		rc = __generic_file_write_iter(iocb, from);  		mutex_unlock(&inode->i_mutex); -	} -	if (rc > 0) { -		ssize_t err; +		if (rc > 0) { +			ssize_t err; -		err = generic_write_sync(file, pos, rc); -		if (err < 0 && rc > 0) -			rc = err; +			err = generic_write_sync(file, iocb->ki_pos - rc, rc); +			if (err < 0) +				rc = err; +		} +	} else { +		mutex_unlock(&inode->i_mutex);  	} -  	up_read(&cinode->lock_sem);  	return rc;  }  ssize_t -cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov, -		   unsigned long nr_segs, loff_t pos) +cifs_strict_writev(struct kiocb *iocb, struct iov_iter *from)  {  	struct inode *inode = file_inode(iocb->ki_filp);  	struct cifsInodeInfo *cinode = CIFS_I(inode); @@ -2580,12 +2617,19 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,  	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);  	ssize_t written; +	written = cifs_get_writer(cinode); +	if (written) +		return written; +  	if (CIFS_CACHE_WRITE(cinode)) {  		if (cap_unix(tcon->ses) &&  		(CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) -		    && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) -			return generic_file_aio_write(iocb, iov, nr_segs, pos); -		return cifs_writev(iocb, iov, nr_segs, pos); +		  && ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) { +			written = generic_file_write_iter(iocb, from); +			goto out; +		} +		written = cifs_writev(iocb, from); +		goto out;  	}  	/*  	 * For non-oplocked files in strict cache mode we need to write the data @@ -2593,18 +2637,20 @@ cifs_strict_writev(struct kiocb *iocb, const struct iovec *iov,  	 * affected pages because it may cause a error with mandatory locks on  	 * these pages but not on the region from pos to ppos+len-1.  	 */ -	written = cifs_user_writev(iocb, iov, nr_segs, pos); +	written = cifs_user_writev(iocb, from);  	if (written > 0 && CIFS_CACHE_READ(cinode)) {  		/*  		 * Windows 7 server can delay breaking level2 oplock if a write  		 * request comes - break it on the client to prevent reading  		 * an old data.  		 */ -		cifs_invalidate_mapping(inode); +		cifs_zap_mapping(inode);  		cifs_dbg(FYI, "Set no oplock for inode=%p after a write operation\n",  			 inode);  		cinode->oplock = 0;  	} +out: +	cifs_put_writer(cinode);  	return written;  } @@ -2699,56 +2745,27 @@ cifs_retry_async_readv(struct cifs_readdata *rdata)  /**   * cifs_readdata_to_iov - copy data from pages in response to an iovec   * @rdata:	the readdata response with list of pages holding data - * @iov:	vector in which we should copy the data - * @nr_segs:	number of segments in vector - * @offset:	offset into file of the first iovec - * @copied:	used to return the amount of data copied to the iov + * @iter:	destination for our data   *   * This function copies data from a list of pages in a readdata response into   * an array of iovecs. It will first calculate where the data should go   * based on the info in the readdata and then copy the data into that spot.   */ -static ssize_t -cifs_readdata_to_iov(struct cifs_readdata *rdata, const struct iovec *iov, -			unsigned long nr_segs, loff_t offset, ssize_t *copied) +static int +cifs_readdata_to_iov(struct cifs_readdata *rdata, struct iov_iter *iter)  { -	int rc = 0; -	struct iov_iter ii; -	size_t pos = rdata->offset - offset; -	ssize_t remaining = rdata->bytes; -	unsigned char *pdata; +	size_t remaining = rdata->bytes;  	unsigned int i; -	/* set up iov_iter and advance to the correct offset */ -	iov_iter_init(&ii, iov, nr_segs, iov_length(iov, nr_segs), 0); -	iov_iter_advance(&ii, pos); - -	*copied = 0;  	for (i = 0; i < rdata->nr_pages; i++) { -		ssize_t copy;  		struct page *page = rdata->pages[i]; - -		/* copy a whole page or whatever's left */ -		copy = min_t(ssize_t, remaining, PAGE_SIZE); - -		/* ...but limit it to whatever space is left in the iov */ -		copy = min_t(ssize_t, copy, iov_iter_count(&ii)); - -		/* go while there's data to be copied and no errors */ -		if (copy && !rc) { -			pdata = kmap(page); -			rc = memcpy_toiovecend(ii.iov, pdata, ii.iov_offset, -						(int)copy); -			kunmap(page); -			if (!rc) { -				*copied += copy; -				remaining -= copy; -				iov_iter_advance(&ii, copy); -			} -		} +		size_t copy = min_t(size_t, remaining, PAGE_SIZE); +		size_t written = copy_page_to_iter(page, 0, copy, iter); +		remaining -= written; +		if (written < copy && iov_iter_count(iter) > 0) +			break;  	} - -	return rc; +	return remaining ? -EFAULT : 0;  }  static void @@ -2809,14 +2826,13 @@ cifs_uncached_read_into_pages(struct TCP_Server_Info *server,  	return total_read > 0 ? total_read : result;  } -static ssize_t -cifs_iovec_read(struct file *file, const struct iovec *iov, -		 unsigned long nr_segs, loff_t *poffset) +ssize_t cifs_user_readv(struct kiocb *iocb, struct iov_iter *to)  { +	struct file *file = iocb->ki_filp;  	ssize_t rc;  	size_t len, cur_len;  	ssize_t total_read = 0; -	loff_t offset = *poffset; +	loff_t offset = iocb->ki_pos;  	unsigned int npages;  	struct cifs_sb_info *cifs_sb;  	struct cifs_tcon *tcon; @@ -2825,10 +2841,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,  	struct list_head rdata_list;  	pid_t pid; -	if (!nr_segs) -		return 0; - -	len = iov_length(iov, nr_segs); +	len = iov_iter_count(to);  	if (!len)  		return 0; @@ -2857,7 +2870,7 @@ cifs_iovec_read(struct file *file, const struct iovec *iov,  					    cifs_uncached_readv_complete);  		if (!rdata) {  			rc = -ENOMEM; -			goto error; +			break;  		}  		rc = cifs_read_allocate_pages(rdata, npages); @@ -2889,60 +2902,48 @@ error:  	if (!list_empty(&rdata_list))  		rc = 0; +	len = iov_iter_count(to);  	/* the loop below should proceed in the order of increasing offsets */ -restart_loop:  	list_for_each_entry_safe(rdata, tmp, &rdata_list, list) { +	again:  		if (!rc) { -			ssize_t copied; -  			/* FIXME: freezable sleep too? */  			rc = wait_for_completion_killable(&rdata->done);  			if (rc)  				rc = -EINTR; -			else if (rdata->result) +			else if (rdata->result) {  				rc = rdata->result; -			else { -				rc = cifs_readdata_to_iov(rdata, iov, -							nr_segs, *poffset, -							&copied); -				total_read += copied; +				/* resend call if it's a retryable error */ +				if (rc == -EAGAIN) { +					rc = cifs_retry_async_readv(rdata); +					goto again; +				} +			} else { +				rc = cifs_readdata_to_iov(rdata, to);  			} -			/* resend call if it's a retryable error */ -			if (rc == -EAGAIN) { -				rc = cifs_retry_async_readv(rdata); -				goto restart_loop; -			}  		}  		list_del_init(&rdata->list);  		kref_put(&rdata->refcount, cifs_uncached_readdata_release);  	} +	total_read = len - iov_iter_count(to); +  	cifs_stats_bytes_read(tcon, total_read); -	*poffset += total_read;  	/* mask nodata case */  	if (rc == -ENODATA)  		rc = 0; -	return total_read ? total_read : rc; -} - -ssize_t cifs_user_readv(struct kiocb *iocb, const struct iovec *iov, -			       unsigned long nr_segs, loff_t pos) -{ -	ssize_t read; - -	read = cifs_iovec_read(iocb->ki_filp, iov, nr_segs, &pos); -	if (read > 0) -		iocb->ki_pos = pos; - -	return read; +	if (total_read) { +		iocb->ki_pos += total_read; +		return total_read; +	} +	return rc;  }  ssize_t -cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov, -		  unsigned long nr_segs, loff_t pos) +cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)  {  	struct inode *inode = file_inode(iocb->ki_filp);  	struct cifsInodeInfo *cinode = CIFS_I(inode); @@ -2961,22 +2962,22 @@ cifs_strict_readv(struct kiocb *iocb, const struct iovec *iov,  	 * pos+len-1.  	 */  	if (!CIFS_CACHE_READ(cinode)) -		return cifs_user_readv(iocb, iov, nr_segs, pos); +		return cifs_user_readv(iocb, to);  	if (cap_unix(tcon->ses) &&  	    (CIFS_UNIX_FCNTL_CAP & le64_to_cpu(tcon->fsUnixInfo.Capability)) &&  	    ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NOPOSIXBRL) == 0)) -		return generic_file_aio_read(iocb, iov, nr_segs, pos); +		return generic_file_read_iter(iocb, to);  	/*  	 * We need to hold the sem to be sure nobody modifies lock list  	 * with a brlock that prevents reading.  	 */  	down_read(&cinode->lock_sem); -	if (!cifs_find_lock_conflict(cfile, pos, iov_length(iov, nr_segs), +	if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),  				     tcon->ses->server->vals->shared_lock_type,  				     NULL, CIFS_READ_OP)) -		rc = generic_file_aio_read(iocb, iov, nr_segs, pos); +		rc = generic_file_read_iter(iocb, to);  	up_read(&cinode->lock_sem);  	return rc;  } @@ -3085,6 +3086,7 @@ cifs_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)  static struct vm_operations_struct cifs_file_vm_ops = {  	.fault = filemap_fault, +	.map_pages = filemap_map_pages,  	.page_mkwrite = cifs_page_mkwrite,  	.remap_pages = generic_file_remap_pages,  }; @@ -3097,7 +3099,7 @@ int cifs_file_strict_mmap(struct file *file, struct vm_area_struct *vma)  	xid = get_xid();  	if (!CIFS_CACHE_READ(CIFS_I(inode))) { -		rc = cifs_invalidate_mapping(inode); +		rc = cifs_zap_mapping(inode);  		if (rc)  			return rc;  	} @@ -3254,6 +3256,9 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,  	/*  	 * Reads as many pages as possible from fscache. Returns -ENOBUFS  	 * immediately if the cookie is negative +	 * +	 * After this point, every page in the list might have PG_fscache set, +	 * so we will need to clean that up off of every page we don't use.  	 */  	rc = cifs_readpages_from_fscache(mapping->host, mapping, page_list,  					 &num_pages); @@ -3376,6 +3381,11 @@ static int cifs_readpages(struct file *file, struct address_space *mapping,  		kref_put(&rdata->refcount, cifs_readdata_release);  	} +	/* Any pages that have been shown to fscache but didn't get added to +	 * the pagecache must be uncached before they get returned to the +	 * allocator. +	 */ +	cifs_fscache_readpages_cancel(mapping->host, page_list);  	return rc;  } @@ -3608,6 +3618,13 @@ static int cifs_launder_page(struct page *page)  	return rc;  } +static int +cifs_pending_writers_wait(void *unused) +{ +	schedule(); +	return 0; +} +  void cifs_oplock_break(struct work_struct *work)  {  	struct cifsFileInfo *cfile = container_of(work, struct cifsFileInfo, @@ -3615,8 +3632,15 @@ void cifs_oplock_break(struct work_struct *work)  	struct inode *inode = cfile->dentry->d_inode;  	struct cifsInodeInfo *cinode = CIFS_I(inode);  	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); +	struct TCP_Server_Info *server = tcon->ses->server;  	int rc = 0; +	wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS, +			cifs_pending_writers_wait, TASK_UNINTERRUPTIBLE); + +	server->ops->downgrade_oplock(server, cinode, +		test_bit(CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, &cinode->flags)); +  	if (!CIFS_CACHE_WRITE(cinode) && CIFS_CACHE_READ(cinode) &&  						cifs_has_mand_locks(cinode)) {  		cifs_dbg(FYI, "Reset oplock to None for inode=%p due to mand locks\n", @@ -3633,7 +3657,7 @@ void cifs_oplock_break(struct work_struct *work)  		if (!CIFS_CACHE_READ(cinode)) {  			rc = filemap_fdatawait(inode->i_mapping);  			mapping_set_error(inode->i_mapping, rc); -			cifs_invalidate_mapping(inode); +			cifs_zap_mapping(inode);  		}  		cifs_dbg(FYI, "Oplock flush inode %p rc %d\n", inode, rc);  	} @@ -3653,8 +3677,30 @@ void cifs_oplock_break(struct work_struct *work)  							     cinode);  		cifs_dbg(FYI, "Oplock release rc = %d\n", rc);  	} +	cifs_done_oplock_break(cinode);  } +/* + * The presence of cifs_direct_io() in the address space ops vector + * allowes open() O_DIRECT flags which would have failed otherwise. + * + * In the non-cached mode (mount with cache=none), we shunt off direct read and write requests + * so this method should never be called. + * + * Direct IO is not yet supported in the cached mode.  + */ +static ssize_t +cifs_direct_io(int rw, struct kiocb *iocb, struct iov_iter *iter, +               loff_t pos) +{ +        /* +         * FIXME +         * Eventually need to support direct IO for non forcedirectio mounts +         */ +        return -EINVAL; +} + +  const struct address_space_operations cifs_addr_ops = {  	.readpage = cifs_readpage,  	.readpages = cifs_readpages, @@ -3664,6 +3710,7 @@ const struct address_space_operations cifs_addr_ops = {  	.write_end = cifs_write_end,  	.set_page_dirty = __set_page_dirty_nobuffers,  	.releasepage = cifs_release_page, +	.direct_IO = cifs_direct_io,  	.invalidatepage = cifs_invalidate_page,  	.launder_page = cifs_launder_page,  }; diff --git a/fs/cifs/fscache.c b/fs/cifs/fscache.c index 2f4bc5a5805..8d4b7bc8ae9 100644 --- a/fs/cifs/fscache.c +++ b/fs/cifs/fscache.c @@ -27,7 +27,7 @@ void cifs_fscache_get_client_cookie(struct TCP_Server_Info *server)  {  	server->fscache =  		fscache_acquire_cookie(cifs_fscache_netfs.primary_index, -				&cifs_fscache_server_index_def, server); +				&cifs_fscache_server_index_def, server, true);  	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",  		 __func__, server, server->fscache);  } @@ -46,7 +46,7 @@ void cifs_fscache_get_super_cookie(struct cifs_tcon *tcon)  	tcon->fscache =  		fscache_acquire_cookie(server->fscache, -				&cifs_fscache_super_index_def, tcon); +				&cifs_fscache_super_index_def, tcon, true);  	cifs_dbg(FYI, "%s: (0x%p/0x%p)\n",  		 __func__, server->fscache, tcon->fscache);  } @@ -69,7 +69,7 @@ static void cifs_fscache_enable_inode_cookie(struct inode *inode)  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE) {  		cifsi->fscache = fscache_acquire_cookie(tcon->fscache, -				&cifs_fscache_inode_object_def, cifsi); +				&cifs_fscache_inode_object_def, cifsi, true);  		cifs_dbg(FYI, "%s: got FH cookie (0x%p/0x%p)\n",  			 __func__, tcon->fscache, cifsi->fscache);  	} @@ -119,7 +119,7 @@ void cifs_fscache_reset_inode_cookie(struct inode *inode)  		cifsi->fscache = fscache_acquire_cookie(  					cifs_sb_master_tcon(cifs_sb)->fscache,  					&cifs_fscache_inode_object_def, -					cifsi); +					cifsi, true);  		cifs_dbg(FYI, "%s: new cookie 0x%p oldcookie 0x%p\n",  			 __func__, cifsi->fscache, old);  	} @@ -223,6 +223,13 @@ void __cifs_readpage_to_fscache(struct inode *inode, struct page *page)  		fscache_uncache_page(CIFS_I(inode)->fscache, page);  } +void __cifs_fscache_readpages_cancel(struct inode *inode, struct list_head *pages) +{ +	cifs_dbg(FYI, "%s: (fsc: %p, i: %p)\n", +		 __func__, CIFS_I(inode)->fscache, inode); +	fscache_readpages_cancel(CIFS_I(inode)->fscache, pages); +} +  void __cifs_fscache_invalidate_page(struct page *page, struct inode *inode)  {  	struct cifsInodeInfo *cifsi = CIFS_I(inode); diff --git a/fs/cifs/fscache.h b/fs/cifs/fscache.h index 63539323e0b..24794b6cd8e 100644 --- a/fs/cifs/fscache.h +++ b/fs/cifs/fscache.h @@ -54,6 +54,7 @@ extern int __cifs_readpages_from_fscache(struct inode *,  					 struct address_space *,  					 struct list_head *,  					 unsigned *); +extern void __cifs_fscache_readpages_cancel(struct inode *, struct list_head *);  extern void __cifs_readpage_to_fscache(struct inode *, struct page *); @@ -91,6 +92,13 @@ static inline void cifs_readpage_to_fscache(struct inode *inode,  		__cifs_readpage_to_fscache(inode, page);  } +static inline void cifs_fscache_readpages_cancel(struct inode *inode, +						 struct list_head *pages) +{ +	if (CIFS_I(inode)->fscache) +		return __cifs_fscache_readpages_cancel(inode, pages); +} +  #else /* CONFIG_CIFS_FSCACHE */  static inline int cifs_fscache_register(void) { return 0; }  static inline void cifs_fscache_unregister(void) {} @@ -131,6 +139,11 @@ static inline int cifs_readpages_from_fscache(struct inode *inode,  static inline void cifs_readpage_to_fscache(struct inode *inode,  			struct page *page) {} +static inline void cifs_fscache_readpages_cancel(struct inode *inode, +						 struct list_head *pages) +{ +} +  #endif /* CONFIG_CIFS_FSCACHE */  #endif /* _CIFS_FSCACHE_H */ diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c index f9ff9c173f7..a174605f6af 100644 --- a/fs/cifs/inode.c +++ b/fs/cifs/inode.c @@ -22,6 +22,7 @@  #include <linux/stat.h>  #include <linux/slab.h>  #include <linux/pagemap.h> +#include <linux/freezer.h>  #include <asm/div64.h>  #include "cifsfs.h"  #include "cifspdu.h" @@ -117,7 +118,34 @@ cifs_revalidate_cache(struct inode *inode, struct cifs_fattr *fattr)  	cifs_dbg(FYI, "%s: invalidating inode %llu mapping\n",  		 __func__, cifs_i->uniqueid); -	cifs_i->invalid_mapping = true; +	set_bit(CIFS_INO_INVALID_MAPPING, &cifs_i->flags); +} + +/* + * copy nlink to the inode, unless it wasn't provided.  Provide + * sane values if we don't have an existing one and none was provided + */ +static void +cifs_nlink_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr) +{ +	/* +	 * if we're in a situation where we can't trust what we +	 * got from the server (readdir, some non-unix cases) +	 * fake reasonable values +	 */ +	if (fattr->cf_flags & CIFS_FATTR_UNKNOWN_NLINK) { +		/* only provide fake values on a new inode */ +		if (inode->i_state & I_NEW) { +			if (fattr->cf_cifsattrs & ATTR_DIRECTORY) +				set_nlink(inode, 2); +			else +				set_nlink(inode, 1); +		} +		return; +	} + +	/* we trust the server, so update it */ +	set_nlink(inode, fattr->cf_nlink);  }  /* populate an inode with info from a cifs_fattr struct */ @@ -134,7 +162,7 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)  	inode->i_mtime = fattr->cf_mtime;  	inode->i_ctime = fattr->cf_ctime;  	inode->i_rdev = fattr->cf_rdev; -	set_nlink(inode, fattr->cf_nlink); +	cifs_nlink_fattr_to_inode(inode, fattr);  	inode->i_uid = fattr->cf_uid;  	inode->i_gid = fattr->cf_gid; @@ -150,7 +178,10 @@ cifs_fattr_to_inode(struct inode *inode, struct cifs_fattr *fattr)  	else  		cifs_i->time = jiffies; -	cifs_i->delete_pending = fattr->cf_flags & CIFS_FATTR_DELETE_PENDING; +	if (fattr->cf_flags & CIFS_FATTR_DELETE_PENDING) +		set_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags); +	else +		clear_bit(CIFS_INO_DELETE_PENDING, &cifs_i->flags);  	cifs_i->server_eof = fattr->cf_eof;  	/* @@ -356,9 +387,10 @@ int cifs_get_inode_info_unix(struct inode **pinode,  	/* check for Minshall+French symlinks */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { -		int tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); +		int tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, +					     full_path);  		if (tmprc) -			cifs_dbg(FYI, "CIFSCheckMFSymlink: %d\n", tmprc); +			cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);  	}  	if (*pinode == NULL) { @@ -376,18 +408,20 @@ int cifs_get_inode_info_unix(struct inode **pinode,  }  static int -cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path, +cifs_sfu_type(struct cifs_fattr *fattr, const char *path,  	      struct cifs_sb_info *cifs_sb, unsigned int xid)  {  	int rc;  	int oplock = 0; -	__u16 netfid;  	struct tcon_link *tlink;  	struct cifs_tcon *tcon; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	struct cifs_io_parms io_parms;  	char buf[24];  	unsigned int bytes_read;  	char *pbuf; +	int buf_type = CIFS_NO_BUFFER;  	pbuf = buf; @@ -408,62 +442,69 @@ cifs_sfu_type(struct cifs_fattr *fattr, const unsigned char *path,  		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 & -				CIFS_MOUNT_MAP_SPECIAL_CHR); -	if (rc == 0) { -		int buf_type = CIFS_NO_BUFFER; -			/* Read header */ -		io_parms.netfid = netfid; -		io_parms.pid = current->tgid; -		io_parms.tcon = tcon; -		io_parms.offset = 0; -		io_parms.length = 24; -		rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, -				 &buf_type); -		if ((rc == 0) && (bytes_read >= 8)) { -			if (memcmp("IntxBLK", pbuf, 8) == 0) { -				cifs_dbg(FYI, "Block device\n"); -				fattr->cf_mode |= S_IFBLK; -				fattr->cf_dtype = DT_BLK; -				if (bytes_read == 24) { -					/* we have enough to decode dev num */ -					__u64 mjr; /* major */ -					__u64 mnr; /* minor */ -					mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); -					mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); -					fattr->cf_rdev = MKDEV(mjr, mnr); -				} -			} else if (memcmp("IntxCHR", pbuf, 8) == 0) { -				cifs_dbg(FYI, "Char device\n"); -				fattr->cf_mode |= S_IFCHR; -				fattr->cf_dtype = DT_CHR; -				if (bytes_read == 24) { -					/* we have enough to decode dev num */ -					__u64 mjr; /* major */ -					__u64 mnr; /* minor */ -					mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); -					mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); -					fattr->cf_rdev = MKDEV(mjr, mnr); -				} -			} else if (memcmp("IntxLNK", pbuf, 7) == 0) { -				cifs_dbg(FYI, "Symlink\n"); -				fattr->cf_mode |= S_IFLNK; -				fattr->cf_dtype = DT_LNK; -			} else { -				fattr->cf_mode |= S_IFREG; /* file? */ -				fattr->cf_dtype = DT_REG; -				rc = -EOPNOTSUPP; +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_READ; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) { +		cifs_put_tlink(tlink); +		return rc; +	} + +	/* Read header */ +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = 24; + +	rc = CIFSSMBRead(xid, &io_parms, &bytes_read, &pbuf, &buf_type); +	if ((rc == 0) && (bytes_read >= 8)) { +		if (memcmp("IntxBLK", pbuf, 8) == 0) { +			cifs_dbg(FYI, "Block device\n"); +			fattr->cf_mode |= S_IFBLK; +			fattr->cf_dtype = DT_BLK; +			if (bytes_read == 24) { +				/* we have enough to decode dev num */ +				__u64 mjr; /* major */ +				__u64 mnr; /* minor */ +				mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); +				mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); +				fattr->cf_rdev = MKDEV(mjr, mnr); +			} +		} else if (memcmp("IntxCHR", pbuf, 8) == 0) { +			cifs_dbg(FYI, "Char device\n"); +			fattr->cf_mode |= S_IFCHR; +			fattr->cf_dtype = DT_CHR; +			if (bytes_read == 24) { +				/* we have enough to decode dev num */ +				__u64 mjr; /* major */ +				__u64 mnr; /* minor */ +				mjr = le64_to_cpu(*(__le64 *)(pbuf+8)); +				mnr = le64_to_cpu(*(__le64 *)(pbuf+16)); +				fattr->cf_rdev = MKDEV(mjr, mnr);  			} +		} else if (memcmp("IntxLNK", pbuf, 7) == 0) { +			cifs_dbg(FYI, "Symlink\n"); +			fattr->cf_mode |= S_IFLNK; +			fattr->cf_dtype = DT_LNK;  		} else { -			fattr->cf_mode |= S_IFREG; /* then it is a file */ +			fattr->cf_mode |= S_IFREG; /* file? */  			fattr->cf_dtype = DT_REG; -			rc = -EOPNOTSUPP; /* or some unknown SFU type */ +			rc = -EOPNOTSUPP;  		} -		CIFSSMBClose(xid, tcon, netfid); +	} else { +		fattr->cf_mode |= S_IFREG; /* then it is a file */ +		fattr->cf_dtype = DT_REG; +		rc = -EOPNOTSUPP; /* or some unknown SFU type */  	} +	CIFSSMBClose(xid, tcon, fid.netfid);  	cifs_put_tlink(tlink);  	return rc;  } @@ -490,10 +531,15 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,  		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); +	if (tcon->ses->server->ops->query_all_EAs == NULL) { +		cifs_put_tlink(tlink); +		return -EOPNOTSUPP; +	} + +	rc = tcon->ses->server->ops->query_all_EAs(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; @@ -515,7 +561,8 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,  /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */  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 cifs_sb_info *cifs_sb, bool adjust_tz, +		       bool symlink)  {  	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); @@ -541,18 +588,20 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,  	fattr->cf_bytes = le64_to_cpu(info->AllocationSize);  	fattr->cf_createtime = le64_to_cpu(info->CreationTime); -	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { +	fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); + +	if (symlink) { +		fattr->cf_mode = S_IFLNK; +		fattr->cf_dtype = DT_LNK; +	} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {  		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;  		fattr->cf_dtype = DT_DIR;  		/*  		 * Server can return wrong NumberOfLinks value for directories  		 * when Unix extensions are disabled - fake it.  		 */ -		fattr->cf_nlink = 2; -	} else if (fattr->cf_cifsattrs & ATTR_REPARSE) { -		fattr->cf_mode = S_IFLNK; -		fattr->cf_dtype = DT_LNK; -		fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); +		if (!tcon->unix_ext) +			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;  	} else {  		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;  		fattr->cf_dtype = DT_REG; @@ -561,11 +610,15 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,  		if (fattr->cf_cifsattrs & ATTR_READONLY)  			fattr->cf_mode &= ~(S_IWUGO); -		fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); -		if (fattr->cf_nlink < 1) { -			cifs_dbg(1, "replacing bogus file nlink value %u\n", +		/* +		 * Don't accept zero nlink from non-unix servers unless +		 * delete is pending.  Instead mark it as unknown. +		 */ +		if ((fattr->cf_nlink < 1) && !tcon->unix_ext && +		    !info->DeletePending) { +			cifs_dbg(1, "bogus file nlink value %u\n",  				fattr->cf_nlink); -			fattr->cf_nlink = 1; +			fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;  		}  	} @@ -593,7 +646,8 @@ cifs_get_file_info(struct file *filp)  	rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);  	switch (rc) {  	case 0: -		cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false); +		cifs_all_info_to_fattr(&fattr, &find_data, cifs_sb, false, +				       false);  		break;  	case -EREMOTE:  		cifs_create_dfs_fattr(&fattr, inode->i_sb); @@ -627,7 +681,7 @@ cgfi_exit:  int  cifs_get_inode_info(struct inode **inode, const char *full_path,  		    FILE_ALL_INFO *data, struct super_block *sb, int xid, -		    const __u16 *fid) +		    const struct cifs_fid *fid)  {  	bool validinum = false;  	__u16 srchflgs; @@ -640,6 +694,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,  	bool adjust_tz = false;  	struct cifs_fattr fattr;  	struct cifs_search_info *srchinf = NULL; +	bool symlink = false;  	tlink = cifs_sb_tlink(cifs_sb);  	if (IS_ERR(tlink)) @@ -669,12 +724,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,  		}  		data = (FILE_ALL_INFO *)buf;  		rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, -						  data, &adjust_tz); +						  data, &adjust_tz, &symlink);  	}  	if (!rc) { -		cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb, -				       adjust_tz); +		cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz, +				       symlink);  	} else if (rc == -EREMOTE) {  		cifs_create_dfs_fattr(&fattr, sb);  		rc = 0; @@ -763,9 +818,10 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,  	/* check for Minshall+French symlinks */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) { -		tmprc = CIFSCheckMFSymlink(&fattr, full_path, cifs_sb, xid); +		tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr, +					 full_path);  		if (tmprc) -			cifs_dbg(FYI, "CIFSCheckMFSymlink: %d\n", tmprc); +			cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);  	}  	if (!*inode) { @@ -994,7 +1050,8 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  {  	int oplock = 0;  	int rc; -	__u16 netfid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	struct inode *inode = dentry->d_inode;  	struct cifsInodeInfo *cifsInode = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); @@ -1017,10 +1074,16 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  		goto out;  	} -	rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, -			 DELETE|FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, -			 &netfid, &oplock, NULL, cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = DELETE | FILE_WRITE_ATTRIBUTES; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc != 0)  		goto out; @@ -1041,7 +1104,7 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  			goto out_close;  		}  		info_buf->Attributes = cpu_to_le32(dosattr); -		rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, +		rc = CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,  					current->tgid);  		/* although we would like to mark the file hidden   		   if that fails we will still try to rename it */ @@ -1052,7 +1115,8 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  	}  	/* rename the file */ -	rc = CIFSSMBRenameOpenFile(xid, tcon, netfid, NULL, cifs_sb->local_nls, +	rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, NULL, +				   cifs_sb->local_nls,  				   cifs_sb->mnt_cifs_flags &  					    CIFS_MOUNT_MAP_SPECIAL_CHR);  	if (rc != 0) { @@ -1061,8 +1125,8 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  	}  	/* try to set DELETE_ON_CLOSE */ -	if (!cifsInode->delete_pending) { -		rc = CIFSSMBSetFileDisposition(xid, tcon, true, netfid, +	if (!test_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags)) { +		rc = CIFSSMBSetFileDisposition(xid, tcon, true, fid.netfid,  					       current->tgid);  		/*  		 * some samba versions return -ENOENT when we try to set the @@ -1078,11 +1142,11 @@ cifs_rename_pending_delete(const char *full_path, struct dentry *dentry,  			rc = -EBUSY;  			goto undo_rename;  		} -		cifsInode->delete_pending = true; +		set_bit(CIFS_INO_DELETE_PENDING, &cifsInode->flags);  	}  out_close: -	CIFSSMBClose(xid, tcon, netfid); +	CIFSSMBClose(xid, tcon, fid.netfid);  out:  	kfree(info_buf);  	cifs_put_tlink(tlink); @@ -1094,13 +1158,13 @@ out:  	 * them anyway.  	 */  undo_rename: -	CIFSSMBRenameOpenFile(xid, tcon, netfid, dentry->d_name.name, +	CIFSSMBRenameOpenFile(xid, tcon, fid.netfid, dentry->d_name.name,  				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &  					    CIFS_MOUNT_MAP_SPECIAL_CHR);  undo_setattr:  	if (dosattr != origattr) {  		info_buf->Attributes = cpu_to_le32(origattr); -		if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, netfid, +		if (!CIFSSMBSetFileInfo(xid, tcon, info_buf, fid.netfid,  					current->tgid))  			cifsInode->cifsAttrs = origattr;  	} @@ -1511,7 +1575,8 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,  	struct tcon_link *tlink;  	struct cifs_tcon *tcon;  	struct TCP_Server_Info *server; -	__u16 srcfid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	int oplock, rc;  	tlink = cifs_sb_tlink(cifs_sb); @@ -1538,17 +1603,23 @@ cifs_do_rename(const unsigned int xid, struct dentry *from_dentry,  	if (to_dentry->d_parent != from_dentry->d_parent)  		goto do_rename_exit; +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb;  	/* open the file to be renamed -- we need DELETE perms */ -	rc = CIFSSMBOpen(xid, tcon, from_path, FILE_OPEN, DELETE, -			 CREATE_NOT_DIR, &srcfid, &oplock, NULL, -			 cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & -				CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.desired_access = DELETE; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = from_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc == 0) { -		rc = CIFSSMBRenameOpenFile(xid, tcon, srcfid, +		rc = CIFSSMBRenameOpenFile(xid, tcon, fid.netfid,  				(const char *) to_dentry->d_name.name,  				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &  					CIFS_MOUNT_MAP_SPECIAL_CHR); -		CIFSSMBClose(xid, tcon, srcfid); +		CIFSSMBClose(xid, tcon, fid.netfid);  	}  do_rename_exit:  	cifs_put_tlink(tlink); @@ -1670,6 +1741,9 @@ cifs_inode_needs_reval(struct inode *inode)  	if (cifs_i->time == 0)  		return true; +	if (!cifs_sb->actimeo) +		return true; +  	if (!time_in_range(jiffies, cifs_i->time,  				cifs_i->time + cifs_sb->actimeo))  		return true; @@ -1689,23 +1763,62 @@ int  cifs_invalidate_mapping(struct inode *inode)  {  	int rc = 0; -	struct cifsInodeInfo *cifs_i = CIFS_I(inode); - -	cifs_i->invalid_mapping = false;  	if (inode->i_mapping && inode->i_mapping->nrpages != 0) {  		rc = invalidate_inode_pages2(inode->i_mapping); -		if (rc) { +		if (rc)  			cifs_dbg(VFS, "%s: could not invalidate inode %p\n",  				 __func__, inode); -			cifs_i->invalid_mapping = true; -		}  	}  	cifs_fscache_reset_inode_cookie(inode);  	return rc;  } +/** + * cifs_wait_bit_killable - helper for functions that are sleeping on bit locks + * @word: long word containing the bit lock + */ +static int +cifs_wait_bit_killable(void *word) +{ +	if (fatal_signal_pending(current)) +		return -ERESTARTSYS; +	freezable_schedule_unsafe(); +	return 0; +} + +int +cifs_revalidate_mapping(struct inode *inode) +{ +	int rc; +	unsigned long *flags = &CIFS_I(inode)->flags; + +	rc = wait_on_bit_lock(flags, CIFS_INO_LOCK, cifs_wait_bit_killable, +				TASK_KILLABLE); +	if (rc) +		return rc; + +	if (test_and_clear_bit(CIFS_INO_INVALID_MAPPING, flags)) { +		rc = cifs_invalidate_mapping(inode); +		if (rc) +			set_bit(CIFS_INO_INVALID_MAPPING, flags); +	} + +	clear_bit_unlock(CIFS_INO_LOCK, flags); +	smp_mb__after_atomic(); +	wake_up_bit(flags, CIFS_INO_LOCK); + +	return rc; +} + +int +cifs_zap_mapping(struct inode *inode) +{ +	set_bit(CIFS_INO_INVALID_MAPPING, &CIFS_I(inode)->flags); +	return cifs_revalidate_mapping(inode); +} +  int cifs_revalidate_file_attr(struct file *filp)  {  	int rc = 0; @@ -1772,9 +1885,7 @@ int cifs_revalidate_file(struct file *filp)  	if (rc)  		return rc; -	if (CIFS_I(inode)->invalid_mapping) -		rc = cifs_invalidate_mapping(inode); -	return rc; +	return cifs_revalidate_mapping(inode);  }  /* revalidate a dentry's inode attributes */ @@ -1787,9 +1898,7 @@ int cifs_revalidate_dentry(struct dentry *dentry)  	if (rc)  		return rc; -	if (CIFS_I(inode)->invalid_mapping) -		rc = cifs_invalidate_mapping(inode); -	return rc; +	return cifs_revalidate_mapping(inode);  }  int cifs_getattr(struct vfsmount *mnt, struct dentry *dentry, diff --git a/fs/cifs/ioctl.c b/fs/cifs/ioctl.c index 3e084558585..45cb59bcc79 100644 --- a/fs/cifs/ioctl.c +++ b/fs/cifs/ioctl.c @@ -3,7 +3,7 @@   *   *   vfs operations that deal with io control   * - *   Copyright (C) International Business Machines  Corp., 2005,2007 + *   Copyright (C) International Business Machines  Corp., 2005,2013   *   Author(s): Steve French (sfrench@us.ibm.com)   *   *   This library is free software; you can redistribute it and/or modify @@ -22,25 +22,132 @@   */  #include <linux/fs.h> +#include <linux/file.h> +#include <linux/mount.h> +#include <linux/mm.h> +#include <linux/pagemap.h>  #include "cifspdu.h"  #include "cifsglob.h"  #include "cifsproto.h"  #include "cifs_debug.h"  #include "cifsfs.h" +#define CIFS_IOCTL_MAGIC	0xCF +#define CIFS_IOC_COPYCHUNK_FILE	_IOW(CIFS_IOCTL_MAGIC, 3, int) + +static long cifs_ioctl_clone(unsigned int xid, struct file *dst_file, +			unsigned long srcfd, u64 off, u64 len, u64 destoff) +{ +	int rc; +	struct cifsFileInfo *smb_file_target = dst_file->private_data; +	struct inode *target_inode = file_inode(dst_file); +	struct cifs_tcon *target_tcon; +	struct fd src_file; +	struct cifsFileInfo *smb_file_src; +	struct inode *src_inode; +	struct cifs_tcon *src_tcon; + +	cifs_dbg(FYI, "ioctl clone range\n"); +	/* the destination must be opened for writing */ +	if (!(dst_file->f_mode & FMODE_WRITE)) { +		cifs_dbg(FYI, "file target not open for write\n"); +		return -EINVAL; +	} + +	/* check if target volume is readonly and take reference */ +	rc = mnt_want_write_file(dst_file); +	if (rc) { +		cifs_dbg(FYI, "mnt_want_write failed with rc %d\n", rc); +		return rc; +	} + +	src_file = fdget(srcfd); +	if (!src_file.file) { +		rc = -EBADF; +		goto out_drop_write; +	} + +	if ((!src_file.file->private_data) || (!dst_file->private_data)) { +		rc = -EBADF; +		cifs_dbg(VFS, "missing cifsFileInfo on copy range src file\n"); +		goto out_fput; +	} + +	rc = -EXDEV; +	smb_file_target = dst_file->private_data; +	smb_file_src = src_file.file->private_data; +	src_tcon = tlink_tcon(smb_file_src->tlink); +	target_tcon = tlink_tcon(smb_file_target->tlink); + +	/* check if source and target are on same tree connection */ +	if (src_tcon != target_tcon) { +		cifs_dbg(VFS, "file copy src and target on different volume\n"); +		goto out_fput; +	} + +	src_inode = file_inode(src_file.file); + +	/* +	 * Note: cifs case is easier than btrfs since server responsible for +	 * checks for proper open modes and file type and if it wants +	 * server could even support copy of range where source = target +	 */ + +	/* so we do not deadlock racing two ioctls on same files */ +	if (target_inode < src_inode) { +		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_PARENT); +		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_CHILD); +	} else { +		mutex_lock_nested(&src_inode->i_mutex, I_MUTEX_PARENT); +		mutex_lock_nested(&target_inode->i_mutex, I_MUTEX_CHILD); +	} + +	/* determine range to clone */ +	rc = -EINVAL; +	if (off + len > src_inode->i_size || off + len < off) +		goto out_unlock; +	if (len == 0) +		len = src_inode->i_size - off; + +	cifs_dbg(FYI, "about to flush pages\n"); +	/* should we flush first and last page first */ +	truncate_inode_pages_range(&target_inode->i_data, destoff, +				   PAGE_CACHE_ALIGN(destoff + len)-1); + +	if (target_tcon->ses->server->ops->clone_range) +		rc = target_tcon->ses->server->ops->clone_range(xid, +			smb_file_src, smb_file_target, off, len, destoff); + +	/* force revalidate of size and timestamps of target file now +	   that target is updated on the server */ +	CIFS_I(target_inode)->time = 0; +out_unlock: +	/* although unlocking in the reverse order from locking is not +	   strictly necessary here it is a little cleaner to be consistent */ +	if (target_inode < src_inode) { +		mutex_unlock(&src_inode->i_mutex); +		mutex_unlock(&target_inode->i_mutex); +	} else { +		mutex_unlock(&target_inode->i_mutex); +		mutex_unlock(&src_inode->i_mutex); +	} +out_fput: +	fdput(src_file); +out_drop_write: +	mnt_drop_write_file(dst_file); +	return rc; +} +  long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)  {  	struct inode *inode = file_inode(filep);  	int rc = -ENOTTY; /* strange error - but the precedent */  	unsigned int xid;  	struct cifs_sb_info *cifs_sb; -#ifdef CONFIG_CIFS_POSIX  	struct cifsFileInfo *pSMBFile = filep->private_data;  	struct cifs_tcon *tcon;  	__u64	ExtAttrBits = 0; -	__u64	ExtAttrMask = 0;  	__u64   caps; -#endif /* CONFIG_CIFS_POSIX */  	xid = get_xid(); @@ -49,13 +156,14 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)  	cifs_sb = CIFS_SB(inode->i_sb);  	switch (command) { -#ifdef CONFIG_CIFS_POSIX  		case FS_IOC_GETFLAGS:  			if (pSMBFile == NULL)  				break;  			tcon = tlink_tcon(pSMBFile->tlink);  			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); +#ifdef CONFIG_CIFS_POSIX  			if (CIFS_UNIX_EXTATTR_CAP & caps) { +				__u64	ExtAttrMask = 0;  				rc = CIFSGetExtAttr(xid, tcon,  						    pSMBFile->fid.netfid,  						    &ExtAttrBits, &ExtAttrMask); @@ -63,29 +171,53 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)  					rc = put_user(ExtAttrBits &  						FS_FL_USER_VISIBLE,  						(int __user *)arg); +				if (rc != EOPNOTSUPP) +					break; +			} +#endif /* CONFIG_CIFS_POSIX */ +			rc = 0; +			if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) { +				/* add in the compressed bit */ +				ExtAttrBits = FS_COMPR_FL; +				rc = put_user(ExtAttrBits & FS_FL_USER_VISIBLE, +					      (int __user *)arg);  			}  			break; -  		case FS_IOC_SETFLAGS:  			if (pSMBFile == NULL)  				break;  			tcon = tlink_tcon(pSMBFile->tlink);  			caps = le64_to_cpu(tcon->fsUnixInfo.Capability); -			if (CIFS_UNIX_EXTATTR_CAP & caps) { -				if (get_user(ExtAttrBits, (int __user *)arg)) { -					rc = -EFAULT; -					break; -				} -				/* -				 * rc = CIFSGetExtAttr(xid, tcon, -				 *		       pSMBFile->fid.netfid, -				 *		       extAttrBits, -				 *		       &ExtAttrMask); -				 */ + +			if (get_user(ExtAttrBits, (int __user *)arg)) { +				rc = -EFAULT; +				break; +			} + +			/* +			 * if (CIFS_UNIX_EXTATTR_CAP & caps) +			 *	rc = CIFSSetExtAttr(xid, tcon, +			 *		       pSMBFile->fid.netfid, +			 *		       extAttrBits, +			 *		       &ExtAttrMask); +			 * if (rc != EOPNOTSUPP) +			 *	break; +			 */ + +			/* Currently only flag we can set is compressed flag */ +			if ((ExtAttrBits & FS_COMPR_FL) == 0) +				break; + +			/* Try to set compress flag */ +			if (tcon->ses->server->ops->set_compression) { +				rc = tcon->ses->server->ops->set_compression( +							xid, tcon, pSMBFile); +				cifs_dbg(FYI, "set compress flag rc %d\n", rc);  			} -			cifs_dbg(FYI, "set flags not implemented yet\n");  			break; -#endif /* CONFIG_CIFS_POSIX */ +		case CIFS_IOC_COPYCHUNK_FILE: +			rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0); +			break;  		default:  			cifs_dbg(FYI, "unsupported ioctl\n");  			break; diff --git a/fs/cifs/link.c b/fs/cifs/link.c index 7e36ceba0c7..68559fd557f 100644 --- a/fs/cifs/link.c +++ b/fs/cifs/link.c @@ -29,6 +29,10 @@  #include "cifs_debug.h"  #include "cifs_fs_sb.h" +/* + * M-F Symlink Functions - Begin + */ +  #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)) @@ -91,10 +95,8 @@ symlink_hash_err:  }  static int -CIFSParseMFSymlink(const u8 *buf, -		   unsigned int buf_len, -		   unsigned int *_link_len, -		   char **_link_str) +parse_mf_symlink(const u8 *buf, unsigned int buf_len, unsigned int *_link_len, +		 char **_link_str)  {  	int rc;  	unsigned int link_len; @@ -137,7 +139,7 @@ CIFSParseMFSymlink(const u8 *buf,  }  static int -CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str) +format_mf_symlink(u8 *buf, unsigned int buf_len, const char *link_str)  {  	int rc;  	unsigned int link_len; @@ -180,214 +182,114 @@ CIFSFormatMFSymlink(u8 *buf, unsigned int buf_len, const char *link_str)  	return 0;  } +bool +couldbe_mf_symlink(const struct cifs_fattr *fattr) +{ +	if (!S_ISREG(fattr->cf_mode)) +		/* 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; +} +  static int -CIFSCreateMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, -		    const char *fromName, const char *toName, -		    struct cifs_sb_info *cifs_sb) +create_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		  struct cifs_sb_info *cifs_sb, const char *fromName, +		  const char *toName)  {  	int rc; -	int oplock = 0; -	int remap; -	int create_options = CREATE_NOT_DIR; -	__u16 netfid = 0;  	u8 *buf;  	unsigned int bytes_written = 0; -	struct cifs_io_parms io_parms; -	struct nls_table *nls_codepage; - -	nls_codepage = cifs_sb->local_nls; -	remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR;  	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; -	} - -	if (backup_cred(cifs_sb)) -		create_options |= CREATE_OPEN_BACKUP_INTENT; - -	rc = CIFSSMBOpen(xid, tcon, fromName, FILE_CREATE, GENERIC_WRITE, -			 create_options, &netfid, &oplock, NULL, -			 nls_codepage, remap); -	if (rc != 0) { -		kfree(buf); -		return rc; -	} - -	io_parms.netfid = netfid; -	io_parms.pid = current->tgid; -	io_parms.tcon = tcon; -	io_parms.offset = 0; -	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; +	rc = format_mf_symlink(buf, CIFS_MF_SYMLINK_FILE_SIZE, toName); +	if (rc) +		goto out; -	rc = CIFSSMBWrite(xid, &io_parms, &bytes_written, buf, NULL, 0); -	CIFSSMBClose(xid, tcon, netfid); -	kfree(buf); -	if (rc != 0) -		return rc; +	rc = tcon->ses->server->ops->create_mf_symlink(xid, tcon, cifs_sb, +					fromName, buf, &bytes_written); +	if (rc) +		goto out;  	if (bytes_written != CIFS_MF_SYMLINK_FILE_SIZE) -		return -EIO; - -	return 0; +		rc = -EIO; +out: +	kfree(buf); +	return rc;  }  static int -CIFSQueryMFSymLink(const unsigned int xid, struct cifs_tcon *tcon, -		   const unsigned char *searchName, char **symlinkinfo, -		   const struct nls_table *nls_codepage, int remap) +query_mf_symlink(const unsigned int xid, struct cifs_tcon *tcon, +		 struct cifs_sb_info *cifs_sb, const unsigned char *path, +		 char **symlinkinfo)  {  	int rc; -	int oplock = 0; -	__u16 netfid = 0; -	u8 *buf; -	char *pbuf; -	unsigned int bytes_read = 0; -	int buf_type = CIFS_NO_BUFFER; +	u8 *buf = NULL;  	unsigned int link_len = 0; -	struct cifs_io_parms io_parms; -	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 != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { -		CIFSSMBClose(xid, tcon, netfid); -		/* it's not a symlink */ -		return -EINVAL; -	} +	unsigned int bytes_read = 0;  	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL);  	if (!buf)  		return -ENOMEM; -	pbuf = buf; -	io_parms.netfid = netfid; -	io_parms.pid = current->tgid; -	io_parms.tcon = tcon; -	io_parms.offset = 0; -	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; -	rc = CIFSSMBRead(xid, &io_parms, &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 -open_query_close_cifs_symlink(const unsigned char *path, char *pbuf, -			unsigned int *pbytes_read, struct cifs_sb_info *cifs_sb, -			unsigned int xid) -{ -	int rc; -	int oplock = 0; -	__u16 netfid = 0; -	struct tcon_link *tlink; -	struct cifs_tcon *ptcon; -	struct cifs_io_parms io_parms; -	int buf_type = CIFS_NO_BUFFER; -	FILE_ALL_INFO file_info; - -	tlink = cifs_sb_tlink(cifs_sb); -	if (IS_ERR(tlink)) -		return PTR_ERR(tlink); -	ptcon = tlink_tcon(tlink); +	if (tcon->ses->server->ops->query_mf_symlink) +		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, +					      cifs_sb, path, buf, &bytes_read); +	else +		rc = -ENOSYS; -	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) { -		cifs_put_tlink(tlink); -		return rc; -	} +	if (rc) +		goto out; -	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) { -		CIFSSMBClose(xid, ptcon, netfid); -		cifs_put_tlink(tlink); -		/* it's not a symlink */ -		return rc; +	if (bytes_read == 0) { /* not a symlink */ +		rc = -EINVAL; +		goto out;  	} -	io_parms.netfid = netfid; -	io_parms.pid = current->tgid; -	io_parms.tcon = ptcon; -	io_parms.offset = 0; -	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; - -	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); -	CIFSSMBClose(xid, ptcon, netfid); -	cifs_put_tlink(tlink); +	rc = parse_mf_symlink(buf, bytes_read, &link_len, symlinkinfo); +out: +	kfree(buf);  	return rc;  } -  int -CIFSCheckMFSymlink(struct cifs_fattr *fattr, -		   const unsigned char *path, -		   struct cifs_sb_info *cifs_sb, unsigned int xid) +check_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		 struct cifs_sb_info *cifs_sb, struct cifs_fattr *fattr, +		 const unsigned char *path)  { -	int rc = 0; +	int rc;  	u8 *buf = NULL;  	unsigned int link_len = 0;  	unsigned int bytes_read = 0; -	struct cifs_tcon *ptcon; -	if (!CIFSCouldBeMFSymlink(fattr)) +	if (!couldbe_mf_symlink(fattr))  		/* it's not a symlink */  		return 0;  	buf = kmalloc(CIFS_MF_SYMLINK_FILE_SIZE, GFP_KERNEL); -	if (!buf) { -		rc = -ENOMEM; -		goto out; -	} +	if (!buf) +		return -ENOMEM; -	ptcon = tlink_tcon(cifs_sb_tlink(cifs_sb)); -	if ((ptcon->ses) && (ptcon->ses->server->ops->query_mf_symlink)) -		rc = ptcon->ses->server->ops->query_mf_symlink(path, buf, -						 &bytes_read, cifs_sb, xid); +	if (tcon->ses->server->ops->query_mf_symlink) +		rc = tcon->ses->server->ops->query_mf_symlink(xid, tcon, +					      cifs_sb, path, buf, &bytes_read);  	else -		goto out; +		rc = -ENOSYS; -	if (rc != 0) +	if (rc)  		goto out;  	if (bytes_read == 0) /* not a symlink */  		goto out; -	rc = CIFSParseMFSymlink(buf, bytes_read, &link_len, NULL); +	rc = parse_mf_symlink(buf, bytes_read, &link_len, NULL);  	if (rc == -EINVAL) {  		/* it's not a symlink */  		rc = 0; @@ -407,6 +309,95 @@ out:  	return rc;  } +/* + * SMB 1.0 Protocol specific functions + */ + +int +cifs_query_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		      struct cifs_sb_info *cifs_sb, const unsigned char *path, +		      char *pbuf, unsigned int *pbytes_read) +{ +	int rc; +	int oplock = 0; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifs_io_parms io_parms; +	int buf_type = CIFS_NO_BUFFER; +	FILE_ALL_INFO file_info; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_READ; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, &file_info); +	if (rc) +		return rc; + +	if (file_info.EndOfFile != cpu_to_le64(CIFS_MF_SYMLINK_FILE_SIZE)) +		/* it's not a symlink */ +		goto out; + +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + +	rc = CIFSSMBRead(xid, &io_parms, pbytes_read, &pbuf, &buf_type); +out: +	CIFSSMBClose(xid, tcon, fid.netfid); +	return rc; +} + +int +cifs_create_mf_symlink(unsigned int xid, struct cifs_tcon *tcon, +		       struct cifs_sb_info *cifs_sb, const unsigned char *path, +		       char *pbuf, unsigned int *pbytes_written) +{ +	int rc; +	int oplock = 0; +	struct cifs_fid fid; +	struct cifs_open_parms oparms; +	struct cifs_io_parms io_parms; +	int create_options = CREATE_NOT_DIR; + +	if (backup_cred(cifs_sb)) +		create_options |= CREATE_OPEN_BACKUP_INTENT; + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = GENERIC_WRITE; +	oparms.create_options = create_options; +	oparms.disposition = FILE_CREATE; +	oparms.path = path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL); +	if (rc) +		return rc; + +	io_parms.netfid = fid.netfid; +	io_parms.pid = current->tgid; +	io_parms.tcon = tcon; +	io_parms.offset = 0; +	io_parms.length = CIFS_MF_SYMLINK_FILE_SIZE; + +	rc = CIFSSMBWrite(xid, &io_parms, pbytes_written, pbuf, NULL, 0); +	CIFSSMBClose(xid, tcon, fid.netfid); +	return rc; +} + +/* + * M-F Symlink Functions - End + */ +  int  cifs_hardlink(struct dentry *old_file, struct inode *inode,  	      struct dentry *direntry) @@ -442,8 +433,10 @@ cifs_hardlink(struct dentry *old_file, struct inode *inode,  						CIFS_MOUNT_MAP_SPECIAL_CHR);  	else {  		server = tcon->ses->server; -		if (!server->ops->create_hardlink) -			return -ENOSYS; +		if (!server->ops->create_hardlink) { +			rc = -ENOSYS; +			goto cifs_hl_exit; +		}  		rc = server->ops->create_hardlink(xid, tcon, from_name, to_name,  						  cifs_sb);  		if ((rc == -EIO) || (rc == -EINVAL)) @@ -534,15 +527,10 @@ cifs_follow_link(struct dentry *direntry, struct nameidata *nd)  	 * 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); +		rc = query_mf_symlink(xid, tcon, cifs_sb, full_path, +				      &target_path); -	if ((rc != 0) && cap_unix(tcon->ses)) -		rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, &target_path, -					     cifs_sb->local_nls); -	else if (rc != 0 && server->ops->query_symlink) +	if (rc != 0 && server->ops->query_symlink)  		rc = server->ops->query_symlink(xid, tcon, full_path,  						&target_path, cifs_sb); @@ -591,8 +579,7 @@ cifs_symlink(struct inode *inode, struct dentry *direntry, const char *symname)  	/* BB what if DFS and this volume is on different share? BB */  	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) -		rc = CIFSCreateMFSymLink(xid, pTcon, full_path, symname, -					cifs_sb); +		rc = create_mf_symlink(xid, pTcon, cifs_sb, full_path, symname);  	else if (pTcon->unix_ext)  		rc = CIFSUnixCreateSymLink(xid, pTcon, full_path, symname,  					   cifs_sb->local_nls); @@ -621,10 +608,3 @@ symlink_exit:  	free_xid(xid);  	return rc;  } - -void cifs_put_link(struct dentry *direntry, struct nameidata *nd, void *cookie) -{ -	char *p = nd_get_link(nd); -	if (!IS_ERR(p)) -		kfree(p); -} diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 138a011633f..3b0c62e622d 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c @@ -278,7 +278,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,  }  static int -check_smb_hdr(struct smb_hdr *smb, __u16 mid) +check_smb_hdr(struct smb_hdr *smb)  {  	/* does it have the right SMB "signature" ? */  	if (*(__le32 *) smb->Protocol != cpu_to_le32(0x424d53ff)) { @@ -287,13 +287,6 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)  		return 1;  	} -	/* Make sure that message ids match */ -	if (mid != smb->Mid) { -		cifs_dbg(VFS, "Mids do not match. received=%u expected=%u\n", -			 smb->Mid, mid); -		return 1; -	} -  	/* if it's a response then accept */  	if (smb->Flags & SMBFLG_RESPONSE)  		return 0; @@ -302,7 +295,8 @@ check_smb_hdr(struct smb_hdr *smb, __u16 mid)  	if (smb->Command == SMB_COM_LOCKING_ANDX)  		return 0; -	cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", smb->Mid); +	cifs_dbg(VFS, "Server sent request, not response. mid=%u\n", +		 get_mid(smb));  	return 1;  } @@ -310,7 +304,6 @@ int  checkSMB(char *buf, unsigned int total_read)  {  	struct smb_hdr *smb = (struct smb_hdr *)buf; -	__u16 mid = smb->Mid;  	__u32 rfclen = be32_to_cpu(smb->smb_buf_length);  	__u32 clc_len;  /* calculated length */  	cifs_dbg(FYI, "checkSMB Length: 0x%x, smb_buf_length: 0x%x\n", @@ -348,7 +341,7 @@ checkSMB(char *buf, unsigned int total_read)  	}  	/* otherwise, there is enough to get to the BCC */ -	if (check_smb_hdr(smb, mid)) +	if (check_smb_hdr(smb))  		return -EIO;  	clc_len = smbCalcSize(smb); @@ -359,6 +352,7 @@ checkSMB(char *buf, unsigned int total_read)  	}  	if (4 + rfclen != clc_len) { +		__u16 mid = get_mid(smb);  		/* check if bcc wrapped around for large read responses */  		if ((rfclen > 64 * 1024) && (rfclen > clc_len)) {  			/* check if lengths match mod 64K */ @@ -366,11 +360,11 @@ checkSMB(char *buf, unsigned int total_read)  				return 0; /* bcc wrapped */  		}  		cifs_dbg(FYI, "Calculated size %u vs length %u mismatch for mid=%u\n", -			 clc_len, 4 + rfclen, smb->Mid); +			 clc_len, 4 + rfclen, mid);  		if (4 + rfclen < clc_len) {  			cifs_dbg(VFS, "RFC1001 size %u smaller than SMB for mid=%u\n", -				 rfclen, smb->Mid); +				 rfclen, mid);  			return -EIO;  		} else if (rfclen > clc_len + 512) {  			/* @@ -383,7 +377,7 @@ checkSMB(char *buf, unsigned int total_read)  			 * data to 512 bytes.  			 */  			cifs_dbg(VFS, "RFC1001 size %u more than 512 bytes larger than SMB for mid=%u\n", -				 rfclen, smb->Mid); +				 rfclen, mid);  			return -EIO;  		}  	} @@ -472,8 +466,22 @@ is_valid_oplock_break(char *buffer, struct TCP_Server_Info *srv)  				cifs_dbg(FYI, "file id match, oplock break\n");  				pCifsInode = CIFS_I(netfile->dentry->d_inode); -				cifs_set_oplock_level(pCifsInode, -					pSMB->OplockLevel ? OPLOCK_READ : 0); +				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, +					&pCifsInode->flags); + +				/* +				 * Set flag if the server downgrades the oplock +				 * to L2 else clear. +				 */ +				if (pSMB->OplockLevel) +					set_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); +				else +					clear_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &pCifsInode->flags); +  				queue_work(cifsiod_wq,  					   &netfile->oplock_break);  				netfile->oplock_break_cancelled = false; @@ -557,6 +565,62 @@ void cifs_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)  		cinode->oplock = 0;  } +static int +cifs_oplock_break_wait(void *unused) +{ +	schedule(); +	return signal_pending(current) ? -ERESTARTSYS : 0; +} + +/* + * We wait for oplock breaks to be processed before we attempt to perform + * writes. + */ +int cifs_get_writer(struct cifsInodeInfo *cinode) +{ +	int rc; + +start: +	rc = wait_on_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK, +				   cifs_oplock_break_wait, TASK_KILLABLE); +	if (rc) +		return rc; + +	spin_lock(&cinode->writers_lock); +	if (!cinode->writers) +		set_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +	cinode->writers++; +	/* Check to see if we have started servicing an oplock break */ +	if (test_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags)) { +		cinode->writers--; +		if (cinode->writers == 0) { +			clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +			wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +		} +		spin_unlock(&cinode->writers_lock); +		goto start; +	} +	spin_unlock(&cinode->writers_lock); +	return 0; +} + +void cifs_put_writer(struct cifsInodeInfo *cinode) +{ +	spin_lock(&cinode->writers_lock); +	cinode->writers--; +	if (cinode->writers == 0) { +		clear_bit(CIFS_INODE_PENDING_WRITERS, &cinode->flags); +		wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_WRITERS); +	} +	spin_unlock(&cinode->writers_lock); +} + +void cifs_done_oplock_break(struct cifsInodeInfo *cinode) +{ +	clear_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, &cinode->flags); +	wake_up_bit(&cinode->flags, CIFS_INODE_PENDING_OPLOCK_BREAK); +} +  bool  backup_cred(struct cifs_sb_info *cifs_sb)  { diff --git a/fs/cifs/netmisc.c b/fs/cifs/netmisc.c index af847e1cf1c..6834b9c3bec 100644 --- a/fs/cifs/netmisc.c +++ b/fs/cifs/netmisc.c @@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {  	{ERRnoaccess, -EACCES},  	{ERRbadfid, -EBADF},  	{ERRbadmcb, -EIO}, -	{ERRnomem, -ENOMEM}, +	{ERRnomem, -EREMOTEIO},  	{ERRbadmem, -EFAULT},  	{ERRbadenv, -EFAULT},  	{ERRbadformat, -EINVAL}, @@ -780,7 +780,9 @@ static const struct {  	ERRDOS, ERRnoaccess, 0xc0000290}, {  	ERRDOS, ERRbadfunc, 0xc000029c}, {  	ERRDOS, ERRsymlink, NT_STATUS_STOPPED_ON_SYMLINK}, { -	ERRDOS, ERRinvlevel, 0x007c0001}, }; +	ERRDOS, ERRinvlevel, 0x007c0001}, { +	0, 0, 0 } +};  /*****************************************************************************   Print an error message from the status code @@ -793,8 +795,8 @@ cifs_print_status(__u32 status_code)  	while (nt_errs[idx].nt_errstr != NULL) {  		if (((nt_errs[idx].nt_errcode) & 0xFFFFFF) ==  		    (status_code & 0xFFFFFF)) { -			printk(KERN_NOTICE "Status code returned 0x%08x %s\n", -				   status_code, nt_errs[idx].nt_errstr); +			pr_notice("Status code returned 0x%08x %s\n", +				  status_code, nt_errs[idx].nt_errstr);  		}  		idx++;  	} @@ -939,8 +941,9 @@ cifs_UnixTimeToNT(struct timespec t)  	return (u64) t.tv_sec * 10000000 + t.tv_nsec/100 + NTFS_TIME_OFFSET;  } -static int total_days_of_prev_months[] = -{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334}; +static const int total_days_of_prev_months[] = { +	0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 +};  struct timespec cnvrtDosUnixTm(__le16 le_date, __le16 le_time, int offset)  { diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c index 42ef03be089..b15862e0f68 100644 --- a/fs/cifs/readdir.c +++ b/fs/cifs/readdir.c @@ -134,22 +134,6 @@ out:  	dput(dentry);  } -/* - * Is it possible that this directory might turn out to be a DFS referral - * once we go to try and use it? - */ -static bool -cifs_dfs_is_possible(struct cifs_sb_info *cifs_sb) -{ -#ifdef CONFIG_CIFS_DFS_UPCALL -	struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); - -	if (tcon->Flags & SMB_SHARE_IS_IN_DFS) -		return true; -#endif -	return false; -} -  static void  cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)  { @@ -159,27 +143,22 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)  	if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {  		fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;  		fattr->cf_dtype = DT_DIR; -		/* -		 * Windows CIFS servers generally make DFS referrals look -		 * like directories in FIND_* responses with the reparse -		 * attribute flag also set (since DFS junctions are -		 * reparse points). We must revalidate at least these -		 * directory inodes before trying to use them (if -		 * they are DFS we will get PATH_NOT_COVERED back -		 * when queried directly and can then try to connect -		 * to the DFS target) -		 */ -		if (cifs_dfs_is_possible(cifs_sb) && -		    (fattr->cf_cifsattrs & ATTR_REPARSE)) -			fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; -	} else if (fattr->cf_cifsattrs & ATTR_REPARSE) { -		fattr->cf_mode = S_IFLNK; -		fattr->cf_dtype = DT_LNK;  	} else {  		fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;  		fattr->cf_dtype = DT_REG;  	} +	/* +	 * We need to revalidate it further to make a decision about whether it +	 * is a symbolic link, DFS referral or a reparse point with a direct +	 * access like junctions, deduplicated files, NFS symlinks. +	 */ +	if (fattr->cf_cifsattrs & ATTR_REPARSE) +		fattr->cf_flags |= CIFS_FATTR_NEED_REVAL; + +	/* non-unix readdir doesn't provide nlink */ +	fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; +  	if (fattr->cf_cifsattrs & ATTR_READONLY)  		fattr->cf_mode &= ~S_IWUGO; @@ -770,7 +749,7 @@ static int cifs_filldir(char *find_entry, struct file *file,  	}  	if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) && -	    CIFSCouldBeMFSymlink(&fattr)) +	    couldbe_mf_symlink(&fattr))  		/*  		 * trying to get the type and mode can be slow,  		 * so just call those regular files for now, and mark diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index 5f99b7f19e7..e87387dbf39 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -32,88 +32,6 @@  #include <linux/slab.h>  #include "cifs_spnego.h" -/* - * Checks if this is the first smb session to be reconnected after - * the socket has been reestablished (so we know whether to use vc 0). - * Called while holding the cifs_tcp_ses_lock, so do not block - */ -static bool is_first_ses_reconnect(struct cifs_ses *ses) -{ -	struct list_head *tmp; -	struct cifs_ses *tmp_ses; - -	list_for_each(tmp, &ses->server->smb_ses_list) { -		tmp_ses = list_entry(tmp, struct cifs_ses, -				     smb_ses_list); -		if (tmp_ses->need_reconnect == false) -			return false; -	} -	/* could not find a session that was already connected, -	   this must be the first one we are reconnecting */ -	return true; -} - -/* - *	vc number 0 is treated specially by some servers, and should be the - *      first one we request.  After that we can use vcnumbers up to maxvcs, - *	one for each smb session (some Windows versions set maxvcs incorrectly - *	so maxvc=1 can be ignored).  If we have too many vcs, we can reuse - *	any vc but zero (some servers reset the connection on vcnum zero) - * - */ -static __le16 get_next_vcnum(struct cifs_ses *ses) -{ -	__u16 vcnum = 0; -	struct list_head *tmp; -	struct cifs_ses *tmp_ses; -	__u16 max_vcs = ses->server->max_vcs; -	__u16 i; -	int free_vc_found = 0; - -	/* Quoting the MS-SMB specification: "Windows-based SMB servers set this -	field to one but do not enforce this limit, which allows an SMB client -	to establish more virtual circuits than allowed by this value ... but -	other server implementations can enforce this limit." */ -	if (max_vcs < 2) -		max_vcs = 0xFFFF; - -	spin_lock(&cifs_tcp_ses_lock); -	if ((ses->need_reconnect) && is_first_ses_reconnect(ses)) -			goto get_vc_num_exit;  /* vcnum will be zero */ -	for (i = ses->server->srv_count - 1; i < max_vcs; i++) { -		if (i == 0) /* this is the only connection, use vc 0 */ -			break; - -		free_vc_found = 1; - -		list_for_each(tmp, &ses->server->smb_ses_list) { -			tmp_ses = list_entry(tmp, struct cifs_ses, -					     smb_ses_list); -			if (tmp_ses->vcnum == i) { -				free_vc_found = 0; -				break; /* found duplicate, try next vcnum */ -			} -		} -		if (free_vc_found) -			break; /* we found a vcnumber that will work - use it */ -	} - -	if (i == 0) -		vcnum = 0; /* for most common case, ie if one smb session, use -			      vc zero.  Also for case when no free vcnum, zero -			      is safest to send (some clients only send zero) */ -	else if (free_vc_found == 0) -		vcnum = 1;  /* we can not reuse vc=0 safely, since some servers -				reset all uids on that, but 1 is ok. */ -	else -		vcnum = i; -	ses->vcnum = vcnum; -get_vc_num_exit: -	spin_unlock(&cifs_tcp_ses_lock); - -	return cpu_to_le16(vcnum); -} -  static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)  {  	__u32 capabilities = 0; @@ -128,7 +46,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)  					CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,  					USHRT_MAX));  	pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); -	pSMB->req.VcNumber = get_next_vcnum(ses); +	pSMB->req.VcNumber = __constant_cpu_to_le16(1);  	/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ @@ -582,9 +500,9 @@ select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)  				return NTLMv2;  			if (global_secflags & CIFSSEC_MAY_NTLM)  				return NTLM; -			/* Fallthrough */  		default: -			return Unspecified; +			/* Fallthrough to attempt LANMAN authentication next */ +			break;  		}  	case CIFS_NEGFLAVOR_LANMAN:  		switch (requested) { diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index 8233b174de3..d1fdfa84870 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -67,7 +67,7 @@ send_nt_cancel(struct TCP_Server_Info *server, void *buf,  	mutex_unlock(&server->srv_mutex);  	cifs_dbg(FYI, "issued NT_CANCEL for mid %u, rc = %d\n", -		 in_buf->Mid, rc); +		 get_mid(in_buf), rc);  	return rc;  } @@ -101,7 +101,7 @@ cifs_find_mid(struct TCP_Server_Info *server, char *buffer)  	spin_lock(&GlobalMid_Lock);  	list_for_each_entry(mid, &server->pending_mid_q, qhead) { -		if (mid->mid == buf->Mid && +		if (compare_mid(mid->mid, buf) &&  		    mid->mid_state == MID_REQUEST_SUBMITTED &&  		    le16_to_cpu(mid->command) == buf->Command) {  			spin_unlock(&GlobalMid_Lock); @@ -372,6 +372,16 @@ coalesce_t2(char *second_buf, struct smb_hdr *target_hdr)  	return 0;  } +static void +cifs_downgrade_oplock(struct TCP_Server_Info *server, +			struct cifsInodeInfo *cinode, bool set_level2) +{ +	if (set_level2) +		cifs_set_oplock_level(cinode, OPLOCK_READ); +	else +		cifs_set_oplock_level(cinode, 0); +} +  static bool  cifs_check_trans2(struct mid_q_entry *mid, struct TCP_Server_Info *server,  		  char *buf, int malformed) @@ -534,10 +544,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,  static int  cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,  		     struct cifs_sb_info *cifs_sb, const char *full_path, -		     FILE_ALL_INFO *data, bool *adjustTZ) +		     FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink)  {  	int rc; +	*symlink = false; +  	/* could do find first instead but this returns more info */  	rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,  			      cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & @@ -554,6 +566,30 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,  						CIFS_MOUNT_MAP_SPECIAL_CHR);  		*adjustTZ = true;  	} + +	if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) { +		int tmprc; +		int oplock = 0; +		struct cifs_fid fid; +		struct cifs_open_parms oparms; + +		oparms.tcon = tcon; +		oparms.cifs_sb = cifs_sb; +		oparms.desired_access = FILE_READ_ATTRIBUTES; +		oparms.create_options = 0; +		oparms.disposition = FILE_OPEN; +		oparms.path = full_path; +		oparms.fid = &fid; +		oparms.reconnect = false; + +		/* Need to check if this is a symbolic link or not */ +		tmprc = CIFS_open(xid, &oparms, &oplock, NULL); +		if (tmprc == -EOPNOTSUPP) +			*symlink = true; +		else +			CIFSSMBClose(xid, tcon, fid.netfid); +	} +  	return rc;  } @@ -686,12 +722,7 @@ cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,  				     oparms->cifs_sb->local_nls,  				     oparms->cifs_sb->mnt_cifs_flags  						& CIFS_MOUNT_MAP_SPECIAL_CHR); -	return CIFSSMBOpen(xid, oparms->tcon, oparms->path, -			   oparms->disposition, oparms->desired_access, -			   oparms->create_options, &oparms->fid->netfid, oplock, -			   buf, oparms->cifs_sb->local_nls, -			   oparms->cifs_sb->mnt_cifs_flags & -						CIFS_MOUNT_MAP_SPECIAL_CHR); +	return CIFS_open(xid, oparms, oplock, buf);  }  static void @@ -742,8 +773,9 @@ smb_set_file_info(struct inode *inode, const char *full_path,  {  	int oplock = 0;  	int rc; -	__u16 netfid;  	__u32 netpid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	struct cifsFileInfo *open_file;  	struct cifsInodeInfo *cinode = CIFS_I(inode);  	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); @@ -753,7 +785,7 @@ smb_set_file_info(struct inode *inode, const char *full_path,  	/* if the file is already open for write, just use that fileid */  	open_file = find_writable_file(cinode, true);  	if (open_file) { -		netfid = open_file->fid.netfid; +		fid.netfid = open_file->fid.netfid;  		netpid = open_file->pid;  		tcon = tlink_tcon(open_file->tlink);  		goto set_via_filehandle; @@ -777,12 +809,17 @@ smb_set_file_info(struct inode *inode, const char *full_path,  		goto out;  	} -	cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); -	rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, -			 SYNCHRONIZE | FILE_WRITE_ATTRIBUTES, CREATE_NOT_DIR, -			 &netfid, &oplock, NULL, cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = SYNCHRONIZE | FILE_WRITE_ATTRIBUTES; +	oparms.create_options = CREATE_NOT_DIR; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; +	cifs_dbg(FYI, "calling SetFileInfo since SetPathInfo for times not supported by this server\n"); +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc != 0) {  		if (rc == -EIO)  			rc = -EINVAL; @@ -792,12 +829,12 @@ smb_set_file_info(struct inode *inode, const char *full_path,  	netpid = current->tgid;  set_via_filehandle: -	rc = CIFSSMBSetFileInfo(xid, tcon, buf, netfid, netpid); +	rc = CIFSSMBSetFileInfo(xid, tcon, buf, fid.netfid, netpid);  	if (!rc)  		cinode->cifsAttrs = le32_to_cpu(buf->Attributes);  	if (open_file == NULL) -		CIFSSMBClose(xid, tcon, netfid); +		CIFSSMBClose(xid, tcon, fid.netfid);  	else  		cifsFileInfo_put(open_file);  out: @@ -807,6 +844,13 @@ out:  }  static int +cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile) +{ +	return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid); +} + +static int  cifs_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,  		     const char *path, struct cifs_sb_info *cifs_sb,  		     struct cifs_fid *fid, __u16 search_flags, @@ -882,33 +926,80 @@ cifs_mand_lock(const unsigned int xid, struct cifsFileInfo *cfile, __u64 offset,  }  static int +cifs_unix_dfs_readlink(const unsigned int xid, struct cifs_tcon *tcon, +		       const unsigned char *searchName, char **symlinkinfo, +		       const struct nls_table *nls_codepage) +{ +#ifdef CONFIG_CIFS_DFS_UPCALL +	int rc; +	unsigned int num_referrals = 0; +	struct dfs_info3_param *referrals = NULL; + +	rc = get_dfs_path(xid, tcon->ses, searchName, nls_codepage, +			  &num_referrals, &referrals, 0); + +	if (!rc && num_referrals > 0) { +		*symlinkinfo = kstrndup(referrals->node_name, +					strlen(referrals->node_name), +					GFP_KERNEL); +		if (!*symlinkinfo) +			rc = -ENOMEM; +		free_dfs_info_array(referrals, num_referrals); +	} +	return rc; +#else /* No DFS support */ +	return -EREMOTE; +#endif +} + +static int  cifs_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,  		   const char *full_path, char **target_path,  		   struct cifs_sb_info *cifs_sb)  {  	int rc;  	int oplock = 0; -	__u16 netfid; +	struct cifs_fid fid; +	struct cifs_open_parms oparms;  	cifs_dbg(FYI, "%s: path: %s\n", __func__, full_path); -	rc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN, -			 FILE_READ_ATTRIBUTES, OPEN_REPARSE_POINT, &netfid, -			 &oplock, NULL, cifs_sb->local_nls, -			 cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +	/* Check for unix extensions */ +	if (cap_unix(tcon->ses)) { +		rc = CIFSSMBUnixQuerySymLink(xid, tcon, full_path, target_path, +					     cifs_sb->local_nls); +		if (rc == -EREMOTE) +			rc = cifs_unix_dfs_readlink(xid, tcon, full_path, +						    target_path, +						    cifs_sb->local_nls); + +		goto out; +	} + +	oparms.tcon = tcon; +	oparms.cifs_sb = cifs_sb; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.create_options = OPEN_REPARSE_POINT; +	oparms.disposition = FILE_OPEN; +	oparms.path = full_path; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = CIFS_open(xid, &oparms, &oplock, NULL);  	if (rc) -		return rc; +		goto out; -	rc = CIFSSMBQuerySymLink(xid, tcon, netfid, target_path, +	rc = CIFSSMBQuerySymLink(xid, tcon, fid.netfid, target_path,  				 cifs_sb->local_nls); -	if (rc) { -		CIFSSMBClose(xid, tcon, netfid); -		return rc; -	} +	if (rc) +		goto out_close;  	convert_delimiter(*target_path, '/'); -	CIFSSMBClose(xid, tcon, netfid); -	cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path); +out_close: +	CIFSSMBClose(xid, tcon, fid.netfid); +out: +	if (!rc) +		cifs_dbg(FYI, "%s: target path: %s\n", __func__, *target_path);  	return rc;  } @@ -938,6 +1029,7 @@ struct smb_version_operations smb1_operations = {  	.clear_stats = cifs_clear_stats,  	.print_stats = cifs_print_stats,  	.is_oplock_break = is_valid_oplock_break, +	.downgrade_oplock = cifs_downgrade_oplock,  	.check_trans2 = cifs_check_trans2,  	.need_neg = cifs_need_neg,  	.negotiate = cifs_negotiate, @@ -956,6 +1048,7 @@ struct smb_version_operations smb1_operations = {  	.set_path_size = CIFSSMBSetEOF,  	.set_file_size = CIFSSMBSetFileSize,  	.set_file_info = smb_set_file_info, +	.set_compression = cifs_set_compression,  	.echo = CIFSSMBEcho,  	.mkdir = CIFSSMBMkDir,  	.mkdir_setinfo = cifs_mkdir_setinfo, @@ -982,8 +1075,18 @@ struct smb_version_operations smb1_operations = {  	.mand_lock = cifs_mand_lock,  	.mand_unlock_range = cifs_unlock_range,  	.push_mand_locks = cifs_push_mandatory_locks, -	.query_mf_symlink = open_query_close_cifs_symlink, +	.query_mf_symlink = cifs_query_mf_symlink, +	.create_mf_symlink = cifs_create_mf_symlink,  	.is_read_op = cifs_is_read_op, +#ifdef CONFIG_CIFS_XATTR +	.query_all_EAs = CIFSSMBQAllEAs, +	.set_EA = CIFSSMBSetEA, +#endif /* CIFS_XATTR */ +#ifdef CONFIG_CIFS_ACL +	.get_acl = get_cifs_acl, +	.get_acl_by_fid = get_cifs_acl_by_fid, +	.set_acl = set_cifs_acl, +#endif /* CIFS_ACL */  };  struct smb_version_values smb1_values = { diff --git a/fs/cifs/smb2glob.h b/fs/cifs/smb2glob.h index c38350851b0..bc0bb9c34f7 100644 --- a/fs/cifs/smb2glob.h +++ b/fs/cifs/smb2glob.h @@ -57,4 +57,7 @@  #define SMB2_CMACAES_SIZE (16)  #define SMB3_SIGNKEY_SIZE (16) +/* Maximum buffer size value we can send with 1 credit */ +#define SMB2_MAX_BUFFER_SIZE 65536 +  #endif	/* _SMB2_GLOB_H */ diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c index 78ff88c467b..84c012a6aba 100644 --- a/fs/cifs/smb2inode.c +++ b/fs/cifs/smb2inode.c @@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)  int  smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,  		     struct cifs_sb_info *cifs_sb, const char *full_path, -		     FILE_ALL_INFO *data, bool *adjust_tz) +		     FILE_ALL_INFO *data, bool *adjust_tz, bool *symlink)  {  	int rc;  	struct smb2_file_all_info *smb2_data;  	*adjust_tz = false; +	*symlink = false;  	smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,  			    GFP_KERNEL); @@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,  		return -ENOMEM;  	rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, -				FILE_READ_ATTRIBUTES, FILE_OPEN, -				OPEN_REPARSE_POINT, smb2_data, -				SMB2_OP_QUERY_INFO); +				FILE_READ_ATTRIBUTES, FILE_OPEN, 0, +				smb2_data, SMB2_OP_QUERY_INFO); +	if (rc == -EOPNOTSUPP) { +		*symlink = true; +		/* Failed on a symbolic link - query a reparse point info */ +		rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, +					FILE_READ_ATTRIBUTES, FILE_OPEN, +					OPEN_REPARSE_POINT, smb2_data, +					SMB2_OP_QUERY_INFO); +	}  	if (rc)  		goto out; diff --git a/fs/cifs/smb2maperror.c b/fs/cifs/smb2maperror.c index 7c2f45c06fc..94bd4fbb13d 100644 --- a/fs/cifs/smb2maperror.c +++ b/fs/cifs/smb2maperror.c @@ -306,7 +306,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {  	{STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"},  	{STATUS_MORE_PROCESSING_REQUIRED, -EIO,  	"STATUS_MORE_PROCESSING_REQUIRED"}, -	{STATUS_NO_MEMORY, -ENOMEM, "STATUS_NO_MEMORY"}, +	{STATUS_NO_MEMORY, -EREMOTEIO, "STATUS_NO_MEMORY"},  	{STATUS_CONFLICTING_ADDRESSES, -EADDRINUSE,  	"STATUS_CONFLICTING_ADDRESSES"},  	{STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c index fb3966265b6..b8021fde987 100644 --- a/fs/cifs/smb2misc.c +++ b/fs/cifs/smb2misc.c @@ -575,9 +575,21 @@ smb2_is_valid_oplock_break(char *buffer, struct TCP_Server_Info *server)  				else  					cfile->oplock_break_cancelled = false; -				server->ops->set_oplock_level(cinode, -				  rsp->OplockLevel ? SMB2_OPLOCK_LEVEL_II : 0, -				  0, NULL); +				set_bit(CIFS_INODE_PENDING_OPLOCK_BREAK, +					&cinode->flags); + +				/* +				 * Set flag if the server downgrades the oplock +				 * to L2 else clear. +				 */ +				if (rsp->OplockLevel) +					set_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &cinode->flags); +				else +					clear_bit( +					   CIFS_INODE_DOWNGRADE_OPLOCK_TO_L2, +					   &cinode->flags);  				queue_work(cifsiod_wq, &cfile->oplock_break); diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index 861b3321414..787844bde38 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -182,11 +182,8 @@ smb2_negotiate_wsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)  	/* start with specified wsize, or default */  	wsize = volume_info->wsize ? volume_info->wsize : CIFS_DEFAULT_IOSIZE;  	wsize = min_t(unsigned int, wsize, server->max_write); -	/* -	 * limit write size to 2 ** 16, because we don't support multicredit -	 * requests now. -	 */ -	wsize = min_t(unsigned int, wsize, 2 << 15); +	/* set it to the maximum buffer size value we can send with 1 credit */ +	wsize = min_t(unsigned int, wsize, SMB2_MAX_BUFFER_SIZE);  	return wsize;  } @@ -200,15 +197,100 @@ smb2_negotiate_rsize(struct cifs_tcon *tcon, struct smb_vol *volume_info)  	/* start with specified rsize, or default */  	rsize = volume_info->rsize ? volume_info->rsize : CIFS_DEFAULT_IOSIZE;  	rsize = min_t(unsigned int, rsize, server->max_read); -	/* -	 * limit write size to 2 ** 16, because we don't support multicredit -	 * requests now. -	 */ -	rsize = min_t(unsigned int, rsize, 2 << 15); +	/* set it to the maximum buffer size value we can send with 1 credit */ +	rsize = min_t(unsigned int, rsize, SMB2_MAX_BUFFER_SIZE);  	return rsize;  } +#ifdef CONFIG_CIFS_STATS2 +static int +SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	unsigned int ret_data_len = 0; +	struct network_interface_info_ioctl_rsp *out_buf; + +	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, +			FSCTL_QUERY_NETWORK_INTERFACE_INFO, true /* is_fsctl */, +			NULL /* no data input */, 0 /* no data input */, +			(char **)&out_buf, &ret_data_len); + +	if ((rc == 0)  && (ret_data_len > 0)) { +		/* Dump info on first interface */ +		cifs_dbg(FYI, "Adapter Capability 0x%x\t", +			le32_to_cpu(out_buf->Capability)); +		cifs_dbg(FYI, "Link Speed %lld\n", +			le64_to_cpu(out_buf->LinkSpeed)); +	} else +		cifs_dbg(VFS, "error %d on ioctl to get interface list\n", rc); + +	return rc; +} +#endif /* STATS2 */ + +static void +smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	__le16 srch_path = 0; /* Null - open root of share */ +	u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); +	if (rc) +		return; + +#ifdef CONFIG_CIFS_STATS2 +	SMB3_request_interfaces(xid, tcon); +#endif /* STATS2 */ + +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_ATTRIBUTE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_DEVICE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_SECTOR_SIZE_INFORMATION); /* SMB3 specific */ +	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	return; +} + +static void +smb2_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc; +	__le16 srch_path = 0; /* Null - open root of share */ +	u8 oplock = SMB2_OPLOCK_LEVEL_NONE; +	struct cifs_open_parms oparms; +	struct cifs_fid fid; + +	oparms.tcon = tcon; +	oparms.desired_access = FILE_READ_ATTRIBUTES; +	oparms.disposition = FILE_OPEN; +	oparms.create_options = 0; +	oparms.fid = &fid; +	oparms.reconnect = false; + +	rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL); +	if (rc) +		return; + +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_ATTRIBUTE_INFORMATION); +	SMB2_QFS_attr(xid, tcon, fid.persistent_fid, fid.volatile_fid, +			FS_DEVICE_INFORMATION); +	SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); +	return; +} +  static int  smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,  			struct cifs_sb_info *cifs_sb, const char *full_path) @@ -304,7 +386,19 @@ smb2_dump_share_caps(struct seq_file *m, struct cifs_tcon *tcon)  		seq_puts(m, " ASYMMETRIC,");  	if (tcon->capabilities == 0)  		seq_puts(m, " None"); +	if (tcon->ss_flags & SSINFO_FLAGS_ALIGNED_DEVICE) +		seq_puts(m, " Aligned,"); +	if (tcon->ss_flags & SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE) +		seq_puts(m, " Partition Aligned,"); +	if (tcon->ss_flags & SSINFO_FLAGS_NO_SEEK_PENALTY) +		seq_puts(m, " SSD,"); +	if (tcon->ss_flags & SSINFO_FLAGS_TRIM_ENABLED) +		seq_puts(m, " TRIM-support,"); +  	seq_printf(m, "\tShare Flags: 0x%x", tcon->share_flags); +	if (tcon->perf_sector_size) +		seq_printf(m, "\tOptimal sector size: 0x%x", +			   tcon->perf_sector_size);  }  static void @@ -394,6 +488,157 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,  }  static int +SMB2_request_res_key(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid, +		     struct copychunk_ioctl *pcchunk) +{ +	int rc; +	unsigned int ret_data_len; +	struct resume_key_req *res_key; + +	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, +			FSCTL_SRV_REQUEST_RESUME_KEY, true /* is_fsctl */, +			NULL, 0 /* no input */, +			(char **)&res_key, &ret_data_len); + +	if (rc) { +		cifs_dbg(VFS, "refcpy ioctl error %d getting resume key\n", rc); +		goto req_res_key_exit; +	} +	if (ret_data_len < sizeof(struct resume_key_req)) { +		cifs_dbg(VFS, "Invalid refcopy resume key length\n"); +		rc = -EINVAL; +		goto req_res_key_exit; +	} +	memcpy(pcchunk->SourceKey, res_key->ResumeKey, COPY_CHUNK_RES_KEY_SIZE); + +req_res_key_exit: +	kfree(res_key); +	return rc; +} + +static int +smb2_clone_range(const unsigned int xid, +			struct cifsFileInfo *srcfile, +			struct cifsFileInfo *trgtfile, u64 src_off, +			u64 len, u64 dest_off) +{ +	int rc; +	unsigned int ret_data_len; +	struct copychunk_ioctl *pcchunk; +	struct copychunk_ioctl_rsp *retbuf = NULL; +	struct cifs_tcon *tcon; +	int chunks_copied = 0; +	bool chunk_sizes_updated = false; + +	pcchunk = kmalloc(sizeof(struct copychunk_ioctl), GFP_KERNEL); + +	if (pcchunk == NULL) +		return -ENOMEM; + +	cifs_dbg(FYI, "in smb2_clone_range - about to call request res key\n"); +	/* Request a key from the server to identify the source of the copy */ +	rc = SMB2_request_res_key(xid, tlink_tcon(srcfile->tlink), +				srcfile->fid.persistent_fid, +				srcfile->fid.volatile_fid, pcchunk); + +	/* Note: request_res_key sets res_key null only if rc !=0 */ +	if (rc) +		goto cchunk_out; + +	/* For now array only one chunk long, will make more flexible later */ +	pcchunk->ChunkCount = __constant_cpu_to_le32(1); +	pcchunk->Reserved = 0; +	pcchunk->Reserved2 = 0; + +	tcon = tlink_tcon(trgtfile->tlink); + +	while (len > 0) { +		pcchunk->SourceOffset = cpu_to_le64(src_off); +		pcchunk->TargetOffset = cpu_to_le64(dest_off); +		pcchunk->Length = +			cpu_to_le32(min_t(u32, len, tcon->max_bytes_chunk)); + +		/* Request server copy to target from src identified by key */ +		rc = SMB2_ioctl(xid, tcon, trgtfile->fid.persistent_fid, +			trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE, +			true /* is_fsctl */, (char *)pcchunk, +			sizeof(struct copychunk_ioctl),	(char **)&retbuf, +			&ret_data_len); +		if (rc == 0) { +			if (ret_data_len != +					sizeof(struct copychunk_ioctl_rsp)) { +				cifs_dbg(VFS, "invalid cchunk response size\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (retbuf->TotalBytesWritten == 0) { +				cifs_dbg(FYI, "no bytes copied\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			/* +			 * Check if server claimed to write more than we asked +			 */ +			if (le32_to_cpu(retbuf->TotalBytesWritten) > +			    le32_to_cpu(pcchunk->Length)) { +				cifs_dbg(VFS, "invalid copy chunk response\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			if (le32_to_cpu(retbuf->ChunksWritten) != 1) { +				cifs_dbg(VFS, "invalid num chunks written\n"); +				rc = -EIO; +				goto cchunk_out; +			} +			chunks_copied++; + +			src_off += le32_to_cpu(retbuf->TotalBytesWritten); +			dest_off += le32_to_cpu(retbuf->TotalBytesWritten); +			len -= le32_to_cpu(retbuf->TotalBytesWritten); + +			cifs_dbg(FYI, "Chunks %d PartialChunk %d Total %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); +		} else if (rc == -EINVAL) { +			if (ret_data_len != sizeof(struct copychunk_ioctl_rsp)) +				goto cchunk_out; + +			cifs_dbg(FYI, "MaxChunks %d BytesChunk %d MaxCopy %d\n", +				le32_to_cpu(retbuf->ChunksWritten), +				le32_to_cpu(retbuf->ChunkBytesWritten), +				le32_to_cpu(retbuf->TotalBytesWritten)); + +			/* +			 * Check if this is the first request using these sizes, +			 * (ie check if copy succeed once with original sizes +			 * and check if the server gave us different sizes after +			 * we already updated max sizes on previous request). +			 * if not then why is the server returning an error now +			 */ +			if ((chunks_copied != 0) || chunk_sizes_updated) +				goto cchunk_out; + +			/* Check that server is not asking us to grow size */ +			if (le32_to_cpu(retbuf->ChunkBytesWritten) < +					tcon->max_bytes_chunk) +				tcon->max_bytes_chunk = +					le32_to_cpu(retbuf->ChunkBytesWritten); +			else +				goto cchunk_out; /* server gave us bogus size */ + +			/* No need to change MaxChunks since already set to 1 */ +			chunk_sizes_updated = true; +		} +	} + +cchunk_out: +	kfree(pcchunk); +	return rc; +} + +static int  smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,  		struct cifs_fid *fid)  { @@ -446,6 +691,14 @@ smb2_set_file_size(const unsigned int xid, struct cifs_tcon *tcon,  }  static int +smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		   struct cifsFileInfo *cfile) +{ +	return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid, +			    cfile->fid.volatile_fid); +} + +static int  smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,  		     const char *path, struct cifs_sb_info *cifs_sb,  		     struct cifs_fid *fid, __u16 search_flags, @@ -652,6 +905,17 @@ smb2_query_symlink(const unsigned int xid, struct cifs_tcon *tcon,  }  static void +smb2_downgrade_oplock(struct TCP_Server_Info *server, +			struct cifsInodeInfo *cinode, bool set_level2) +{ +	if (set_level2) +		server->ops->set_oplock_level(cinode, SMB2_OPLOCK_LEVEL_II, +						0, NULL); +	else +		server->ops->set_oplock_level(cinode, 0, 0, NULL); +} + +static void  smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock,  		      unsigned int epoch, bool *purge_cache)  { @@ -783,6 +1047,7 @@ smb2_create_lease_buf(u8 *lease_key, u8 oplock)  	buf->ccontext.NameOffset = cpu_to_le16(offsetof  				(struct create_lease, Name));  	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */  	buf->Name[0] = 'R';  	buf->Name[1] = 'q';  	buf->Name[2] = 'L'; @@ -809,6 +1074,7 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock)  	buf->ccontext.NameOffset = cpu_to_le16(offsetof  				(struct create_lease_v2, Name));  	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_REQUEST_LEASE is "RqLs" */  	buf->Name[0] = 'R';  	buf->Name[1] = 'q';  	buf->Name[2] = 'L'; @@ -857,6 +1123,7 @@ struct smb_version_operations smb20_operations = {  	.clear_stats = smb2_clear_stats,  	.print_stats = smb2_print_stats,  	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock,  	.need_neg = smb2_need_neg,  	.negotiate = smb2_negotiate,  	.negotiate_wsize = smb2_negotiate_wsize, @@ -865,6 +1132,7 @@ struct smb_version_operations smb20_operations = {  	.logoff = SMB2_logoff,  	.tree_connect = SMB2_tcon,  	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb2_qfs_tcon,  	.is_path_accessible = smb2_is_path_accessible,  	.can_echo = smb2_can_echo,  	.echo = SMB2_echo, @@ -874,6 +1142,7 @@ struct smb_version_operations smb20_operations = {  	.set_path_size = smb2_set_path_size,  	.set_file_size = smb2_set_file_size,  	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression,  	.mkdir = smb2_mkdir,  	.mkdir_setinfo = smb2_mkdir_setinfo,  	.rmdir = smb2_rmdir, @@ -907,6 +1176,7 @@ struct smb_version_operations smb20_operations = {  	.set_oplock_level = smb2_set_oplock_level,  	.create_lease_buf = smb2_create_lease_buf,  	.parse_lease_buf = smb2_parse_lease_buf, +	.clone_range = smb2_clone_range,  };  struct smb_version_operations smb21_operations = { @@ -928,6 +1198,7 @@ struct smb_version_operations smb21_operations = {  	.clear_stats = smb2_clear_stats,  	.print_stats = smb2_print_stats,  	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock,  	.need_neg = smb2_need_neg,  	.negotiate = smb2_negotiate,  	.negotiate_wsize = smb2_negotiate_wsize, @@ -936,6 +1207,7 @@ struct smb_version_operations smb21_operations = {  	.logoff = SMB2_logoff,  	.tree_connect = SMB2_tcon,  	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb2_qfs_tcon,  	.is_path_accessible = smb2_is_path_accessible,  	.can_echo = smb2_can_echo,  	.echo = SMB2_echo, @@ -945,6 +1217,7 @@ struct smb_version_operations smb21_operations = {  	.set_path_size = smb2_set_path_size,  	.set_file_size = smb2_set_file_size,  	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression,  	.mkdir = smb2_mkdir,  	.mkdir_setinfo = smb2_mkdir_setinfo,  	.rmdir = smb2_rmdir, @@ -978,6 +1251,7 @@ struct smb_version_operations smb21_operations = {  	.set_oplock_level = smb21_set_oplock_level,  	.create_lease_buf = smb2_create_lease_buf,  	.parse_lease_buf = smb2_parse_lease_buf, +	.clone_range = smb2_clone_range,  };  struct smb_version_operations smb30_operations = { @@ -1000,6 +1274,7 @@ struct smb_version_operations smb30_operations = {  	.print_stats = smb2_print_stats,  	.dump_share_caps = smb2_dump_share_caps,  	.is_oplock_break = smb2_is_valid_oplock_break, +	.downgrade_oplock = smb2_downgrade_oplock,  	.need_neg = smb2_need_neg,  	.negotiate = smb2_negotiate,  	.negotiate_wsize = smb2_negotiate_wsize, @@ -1008,6 +1283,7 @@ struct smb_version_operations smb30_operations = {  	.logoff = SMB2_logoff,  	.tree_connect = SMB2_tcon,  	.tree_disconnect = SMB2_tdis, +	.qfs_tcon = smb3_qfs_tcon,  	.is_path_accessible = smb2_is_path_accessible,  	.can_echo = smb2_can_echo,  	.echo = SMB2_echo, @@ -1017,6 +1293,7 @@ struct smb_version_operations smb30_operations = {  	.set_path_size = smb2_set_path_size,  	.set_file_size = smb2_set_file_size,  	.set_file_info = smb2_set_file_info, +	.set_compression = smb2_set_compression,  	.mkdir = smb2_mkdir,  	.mkdir_setinfo = smb2_mkdir_setinfo,  	.rmdir = smb2_rmdir, @@ -1051,6 +1328,8 @@ struct smb_version_operations smb30_operations = {  	.set_oplock_level = smb3_set_oplock_level,  	.create_lease_buf = smb3_create_lease_buf,  	.parse_lease_buf = smb3_parse_lease_buf, +	.clone_range = smb2_clone_range, +	.validate_negotiate = smb3_validate_negotiate,  };  struct smb_version_values smb20_values = { diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index eba0efde66d..b0b260dbb19 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -375,7 +375,12 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)  	req->Capabilities = cpu_to_le32(ses->server->vals->req_capabilities); -	memcpy(req->ClientGUID, cifs_client_guid, SMB2_CLIENT_GUID_SIZE); +	/* ClientGUID must be zero for SMB2.02 dialect */ +	if (ses->server->vals->protocol_id == SMB20_PROT_ID) +		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE); +	else +		memcpy(req->ClientGUID, server->client_guid, +			SMB2_CLIENT_GUID_SIZE);  	iov[0].iov_base = (char *)req;  	/* 4 for rfc1002 length field */ @@ -413,7 +418,9 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)  	/* SMB2 only has an extended negflavor */  	server->negflavor = CIFS_NEGFLAVOR_EXTENDED; -	server->maxBuf = le32_to_cpu(rsp->MaxTransactSize); +	/* set it to the maximum buffer size value we can send with 1 credit */ +	server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize), +			       SMB2_MAX_BUFFER_SIZE);  	server->max_read = le32_to_cpu(rsp->MaxReadSize);  	server->max_write = le32_to_cpu(rsp->MaxWriteSize);  	/* BB Do we need to validate the SecurityMode? */ @@ -454,6 +461,82 @@ neg_exit:  	return rc;  } +int smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon) +{ +	int rc = 0; +	struct validate_negotiate_info_req vneg_inbuf; +	struct validate_negotiate_info_rsp *pneg_rsp; +	u32 rsplen; + +	cifs_dbg(FYI, "validate negotiate\n"); + +	/* +	 * validation ioctl must be signed, so no point sending this if we +	 * can not sign it.  We could eventually change this to selectively +	 * sign just this, the first and only signed request on a connection. +	 * This is good enough for now since a user who wants better security +	 * would also enable signing on the mount. Having validation of +	 * negotiate info for signed connections helps reduce attack vectors +	 */ +	if (tcon->ses->server->sign == false) +		return 0; /* validation requires signing */ + +	vneg_inbuf.Capabilities = +			cpu_to_le32(tcon->ses->server->vals->req_capabilities); +	memcpy(vneg_inbuf.Guid, tcon->ses->server->client_guid, +					SMB2_CLIENT_GUID_SIZE); + +	if (tcon->ses->sign) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED); +	else if (global_secflags & CIFSSEC_MAY_SIGN) +		vneg_inbuf.SecurityMode = +			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED); +	else +		vneg_inbuf.SecurityMode = 0; + +	vneg_inbuf.DialectCount = cpu_to_le16(1); +	vneg_inbuf.Dialects[0] = +		cpu_to_le16(tcon->ses->server->vals->protocol_id); + +	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID, +		FSCTL_VALIDATE_NEGOTIATE_INFO, true /* is_fsctl */, +		(char *)&vneg_inbuf, sizeof(struct validate_negotiate_info_req), +		(char **)&pneg_rsp, &rsplen); + +	if (rc != 0) { +		cifs_dbg(VFS, "validate protocol negotiate failed: %d\n", rc); +		return -EIO; +	} + +	if (rsplen != sizeof(struct validate_negotiate_info_rsp)) { +		cifs_dbg(VFS, "invalid size of protocol negotiate response\n"); +		return -EIO; +	} + +	/* check validate negotiate info response matches what we got earlier */ +	if (pneg_rsp->Dialect != +			cpu_to_le16(tcon->ses->server->vals->protocol_id)) +		goto vneg_out; + +	if (pneg_rsp->SecurityMode != cpu_to_le16(tcon->ses->server->sec_mode)) +		goto vneg_out; + +	/* do not validate server guid because not saved at negprot time yet */ + +	if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND | +	      SMB2_LARGE_FILES) != tcon->ses->server->capabilities) +		goto vneg_out; + +	/* validate negotiate successful */ +	cifs_dbg(FYI, "validate negotiate info successful\n"); +	return 0; + +vneg_out: +	cifs_dbg(VFS, "protocol revalidation - security settings mismatch\n"); +	return -EIO; +} +  int  SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,  		const struct nls_table *nls_cp) @@ -630,6 +713,8 @@ ssetup_ntlmssp_authenticate:  		goto ssetup_exit;  	ses->session_flags = le16_to_cpu(rsp->SessionFlags); +	if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) +		cifs_dbg(VFS, "SMB3 encryption not supported yet\n");  ssetup_exit:  	free_rsp_buf(resp_buftype, rsp); @@ -687,6 +772,10 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)  	else  		return -EIO; +	/* no need to send SMB logoff if uid already closed due to reconnect */ +	if (ses->need_reconnect) +		goto smb2_session_already_dead; +  	rc = small_smb2_init(SMB2_LOGOFF, NULL, (void **) &req);  	if (rc)  		return rc; @@ -701,6 +790,8 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)  	 * No tcon so can't do  	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);  	 */ + +smb2_session_already_dead:  	return rc;  } @@ -711,6 +802,14 @@ static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code)  #define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */) +/* These are similar values to what Windows uses */ +static inline void init_copy_chunk_defaults(struct cifs_tcon *tcon) +{ +	tcon->max_chunks = 256; +	tcon->max_bytes_chunk = 1048576; +	tcon->max_bytes_copy = 16777216; +} +  int  SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,  	  struct cifs_tcon *tcon, const struct nls_table *cp) @@ -812,7 +911,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,  	if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&  	    ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))  		cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); - +	init_copy_chunk_defaults(tcon); +	if (tcon->ses->server->ops->validate_negotiate) +		rc = tcon->ses->server->ops->validate_negotiate(xid, tcon);  tcon_exit:  	free_rsp_buf(resp_buftype, rsp);  	kfree(unc_path); @@ -871,6 +972,7 @@ create_durable_buf(void)  	buf->ccontext.NameOffset = cpu_to_le16(offsetof  				(struct create_durable, Name));  	buf->ccontext.NameLength = cpu_to_le16(4); +	/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */  	buf->Name[0] = 'D';  	buf->Name[1] = 'H';  	buf->Name[2] = 'n'; @@ -895,6 +997,7 @@ create_reconnect_durable_buf(struct cifs_fid *fid)  	buf->ccontext.NameLength = cpu_to_le16(4);  	buf->Data.Fid.PersistentFileId = fid->persistent_fid;  	buf->Data.Fid.VolatileFileId = fid->volatile_fid; +	/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */  	buf->Name[0] = 'D';  	buf->Name[1] = 'H';  	buf->Name[2] = 'n'; @@ -994,6 +1097,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,  	int rc = 0;  	unsigned int num_iovecs = 2;  	__u32 file_attributes = 0; +	char *dhc_buf = NULL, *lc_buf = NULL;  	cifs_dbg(FYI, "create/open\n"); @@ -1060,6 +1164,7 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,  			kfree(copy_path);  			return rc;  		} +		lc_buf = iov[num_iovecs-1].iov_base;  	}  	if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) { @@ -1074,9 +1179,10 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,  		if (rc) {  			cifs_small_buf_release(req);  			kfree(copy_path); -			kfree(iov[num_iovecs-1].iov_base); +			kfree(lc_buf);  			return rc;  		} +		dhc_buf = iov[num_iovecs-1].iov_base;  	}  	rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); @@ -1108,6 +1214,8 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,  		*oplock = rsp->OplockLevel;  creat_exit:  	kfree(copy_path); +	kfree(lc_buf); +	kfree(dhc_buf);  	free_rsp_buf(resp_buftype, rsp);  	return rc;  } @@ -1131,6 +1239,7 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,  	cifs_dbg(FYI, "SMB2 IOCTL\n"); +	*out_data = NULL;  	/* zero out returned data len, in case of error */  	if (plen)  		*plen = 0; @@ -1176,19 +1285,38 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,  		req->Flags = 0;  	iov[0].iov_base = (char *)req; -	/* 4 for rfc1002 length field */ -	iov[0].iov_len = get_rfc1002_length(req) + 4; -	if (indatalen) -		inc_rfc1001_len(req, indatalen); +	/* +	 * If no input data, the size of ioctl struct in +	 * protocol spec still includes a 1 byte data buffer, +	 * but if input data passed to ioctl, we do not +	 * want to double count this, so we do not send +	 * the dummy one byte of data in iovec[0] if sending +	 * input data (in iovec[1]). We also must add 4 bytes +	 * in first iovec to allow for rfc1002 length field. +	 */ + +	if (indatalen) { +		iov[0].iov_len = get_rfc1002_length(req) + 4 - 1; +		inc_rfc1001_len(req, indatalen - 1); +	} else +		iov[0].iov_len = get_rfc1002_length(req) + 4; +  	rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);  	rsp = (struct smb2_ioctl_rsp *)iov[0].iov_base; -	if (rc != 0) { +	if ((rc != 0) && (rc != -EINVAL)) {  		if (tcon)  			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);  		goto ioctl_exit; +	} else if (rc == -EINVAL) { +		if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) && +		    (opcode != FSCTL_SRV_COPYCHUNK)) { +			if (tcon) +				cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE); +			goto ioctl_exit; +		}  	}  	/* check if caller wants to look at return data or just return rc */ @@ -1228,6 +1356,31 @@ ioctl_exit:  	return rc;  } +/* + *   Individual callers to ioctl worker function follow + */ + +int +SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +		     u64 persistent_fid, u64 volatile_fid) +{ +	int rc; +	struct  compress_ioctl fsctl_input; +	char *ret_data = NULL; + +	fsctl_input.CompressionState = +			__constant_cpu_to_le16(COMPRESSION_FORMAT_DEFAULT); + +	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid, +			FSCTL_SET_COMPRESSION, true /* is_fsctl */, +			(char *)&fsctl_input /* data input */, +			2 /* in data len */, &ret_data /* out data */, NULL); + +	cifs_dbg(FYI, "set compression rc %d\n", rc); + +	return rc; +} +  int  SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,  	   u64 persistent_fid, u64 volatile_fid) @@ -1750,7 +1903,8 @@ smb2_writev_callback(struct mid_q_entry *mid)  /* smb2_async_writev - send an async write, and set up mid to handle result */  int -smb2_async_writev(struct cifs_writedata *wdata) +smb2_async_writev(struct cifs_writedata *wdata, +		  void (*release)(struct kref *kref))  {  	int rc = -EACCES;  	struct smb2_write_req *req = NULL; @@ -1798,7 +1952,7 @@ smb2_async_writev(struct cifs_writedata *wdata)  				smb2_writev_callback, wdata, 0);  	if (rc) { -		kref_put(&wdata->refcount, cifs_writedata_release); +		kref_put(&wdata->refcount, release);  		cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);  	} @@ -2098,11 +2252,9 @@ send_set_info(const unsigned int xid, struct cifs_tcon *tcon,  	rc = SendReceive2(xid, ses, iov, num, &resp_buftype, 0);  	rsp = (struct smb2_set_info_rsp *)iov[0].iov_base; -	if (rc != 0) { +	if (rc != 0)  		cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE); -		goto out; -	} -out: +  	free_rsp_buf(resp_buftype, rsp);  	kfree(iov);  	return rc; @@ -2293,7 +2445,7 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,  	rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0);  	if (rc) {  		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); -		goto qinf_exit; +		goto qfsinf_exit;  	}  	rsp = (struct smb2_query_info_rsp *)iov.iov_base; @@ -2305,7 +2457,70 @@ SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,  	if (!rc)  		copy_fs_info_to_kstatfs(info, fsdata); -qinf_exit: +qfsinf_exit: +	free_rsp_buf(resp_buftype, iov.iov_base); +	return rc; +} + +int +SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, +	      u64 persistent_fid, u64 volatile_fid, int level) +{ +	struct smb2_query_info_rsp *rsp = NULL; +	struct kvec iov; +	int rc = 0; +	int resp_buftype, max_len, min_len; +	struct cifs_ses *ses = tcon->ses; +	unsigned int rsp_len, offset; + +	if (level == FS_DEVICE_INFORMATION) { +		max_len = sizeof(FILE_SYSTEM_DEVICE_INFO); +		min_len = sizeof(FILE_SYSTEM_DEVICE_INFO); +	} else if (level == FS_ATTRIBUTE_INFORMATION) { +		max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO); +		min_len = MIN_FS_ATTR_INFO_SIZE; +	} else if (level == FS_SECTOR_SIZE_INFORMATION) { +		max_len = sizeof(struct smb3_fs_ss_info); +		min_len = sizeof(struct smb3_fs_ss_info); +	} else { +		cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level); +		return -EINVAL; +	} + +	rc = build_qfs_info_req(&iov, tcon, level, max_len, +				persistent_fid, volatile_fid); +	if (rc) +		return rc; + +	rc = SendReceive2(xid, ses, &iov, 1, &resp_buftype, 0); +	if (rc) { +		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE); +		goto qfsattr_exit; +	} +	rsp = (struct smb2_query_info_rsp *)iov.iov_base; + +	rsp_len = le32_to_cpu(rsp->OutputBufferLength); +	offset = le16_to_cpu(rsp->OutputBufferOffset); +	rc = validate_buf(offset, rsp_len, &rsp->hdr, min_len); +	if (rc) +		goto qfsattr_exit; + +	if (level == FS_ATTRIBUTE_INFORMATION) +		memcpy(&tcon->fsAttrInfo, 4 /* RFC1001 len */ + offset +			+ (char *)&rsp->hdr, min_t(unsigned int, +			rsp_len, max_len)); +	else if (level == FS_DEVICE_INFORMATION) +		memcpy(&tcon->fsDevInfo, 4 /* RFC1001 len */ + offset +			+ (char *)&rsp->hdr, sizeof(FILE_SYSTEM_DEVICE_INFO)); +	else if (level == FS_SECTOR_SIZE_INFORMATION) { +		struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *) +			(4 /* RFC1001 len */ + offset + (char *)&rsp->hdr); +		tcon->ss_flags = le32_to_cpu(ss_info->Flags); +		tcon->perf_sector_size = +			le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf); +	} + +qfsattr_exit:  	free_rsp_buf(resp_buftype, iov.iov_base);  	return rc;  } diff --git a/fs/cifs/smb2pdu.h b/fs/cifs/smb2pdu.h index b83d0118a75..69f3595d395 100644 --- a/fs/cifs/smb2pdu.h +++ b/fs/cifs/smb2pdu.h @@ -122,6 +122,23 @@ struct smb2_pdu {  	__le16 StructureSize2; /* size of wct area (varies, request specific) */  } __packed; +struct smb2_transform_hdr { +	__be32 smb2_buf_length;	/* big endian on wire */ +				/* length is only two or three bytes - with +				 one or two byte type preceding it that MBZ */ +	__u8   ProtocolId[4];	/* 0xFD 'S' 'M' 'B' */ +	__u8   Signature[16]; +	__u8   Nonce[11]; +	__u8   Reserved[5]; +	__le32 OriginalMessageSize; +	__u16  Reserved1; +	__le16 EncryptionAlgorithm; +	__u64  SessionId; +} __packed; + +/* Encryption Algorithms */ +#define SMB2_ENCRYPTION_AES128_CCM	__constant_cpu_to_le16(0x0001) +  /*   *	SMB2 flag definitions   */ @@ -166,8 +183,6 @@ struct smb2_symlink_err_rsp {  #define SMB2_CLIENT_GUID_SIZE 16 -extern __u8 cifs_client_guid[SMB2_CLIENT_GUID_SIZE]; -  struct smb2_negotiate_req {  	struct smb2_hdr hdr;  	__le16 StructureSize; /* Must be 36 */ @@ -237,6 +252,7 @@ struct smb2_sess_setup_req {  /* Currently defined SessionFlags */  #define SMB2_SESSION_FLAG_IS_GUEST	0x0001  #define SMB2_SESSION_FLAG_IS_NULL	0x0002 +#define SMB2_SESSION_FLAG_ENCRYPT_DATA	0x0004  struct smb2_sess_setup_rsp {  	struct smb2_hdr hdr;  	__le16 StructureSize; /* Must be 9 */ @@ -419,11 +435,15 @@ struct smb2_tree_disconnect_rsp {  #define SMB2_CREATE_SD_BUFFER			"SecD" /* security descriptor */  #define SMB2_CREATE_DURABLE_HANDLE_REQUEST	"DHnQ"  #define SMB2_CREATE_DURABLE_HANDLE_RECONNECT	"DHnC" -#define SMB2_CREATE_ALLOCATION_SIZE		"AlSi" +#define SMB2_CREATE_ALLOCATION_SIZE		"AISi"  #define SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST "MxAc"  #define SMB2_CREATE_TIMEWARP_REQUEST		"TWrp"  #define SMB2_CREATE_QUERY_ON_DISK_ID		"QFid"  #define SMB2_CREATE_REQUEST_LEASE		"RqLs" +#define SMB2_CREATE_DURABLE_HANDLE_REQUEST_V2	"DH2Q" +#define SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2	"DH2C" +#define SMB2_CREATE_APP_INSTANCE_ID	0x45BCA66AEFA7F74A9008FA462E144D74 +#define SVHDX_OPEN_DEVICE_CONTEXT	0x83CE6F1AD851E0986E34401CC9BCFCE9  struct smb2_create_req {  	struct smb2_hdr hdr; @@ -534,9 +554,16 @@ struct create_durable {  	} Data;  } __packed; +#define COPY_CHUNK_RES_KEY_SIZE	24 +struct resume_key_req { +	char ResumeKey[COPY_CHUNK_RES_KEY_SIZE]; +	__le32	ContextLength;	/* MBZ */ +	char	Context[0];	/* ignored, Windows sets to 4 bytes of zero */ +} __packed; +  /* this goes in the ioctl buffer when doing a copychunk request */  struct copychunk_ioctl { -	char SourceKey[24]; +	char SourceKey[COPY_CHUNK_RES_KEY_SIZE];  	__le32 ChunkCount; /* we are only sending 1 */  	__le32 Reserved;  	/* array will only be one chunk long for us */ @@ -546,13 +573,25 @@ struct copychunk_ioctl {  	__u32 Reserved2;  } __packed; -/* Response and Request are the same format */ -struct validate_negotiate_info { +struct copychunk_ioctl_rsp { +	__le32 ChunksWritten; +	__le32 ChunkBytesWritten; +	__le32 TotalBytesWritten; +} __packed; + +struct validate_negotiate_info_req {  	__le32 Capabilities;  	__u8   Guid[SMB2_CLIENT_GUID_SIZE];  	__le16 SecurityMode;  	__le16 DialectCount; -	__le16 Dialect[1]; +	__le16 Dialects[1]; /* dialect (someday maybe list) client asked for */ +} __packed; + +struct validate_negotiate_info_rsp { +	__le32 Capabilities; +	__u8   Guid[SMB2_CLIENT_GUID_SIZE]; +	__le16 SecurityMode; +	__le16 Dialect; /* Dialect in use for the connection */  } __packed;  #define RSS_CAPABLE	0x00000001 @@ -569,6 +608,10 @@ struct network_interface_info_ioctl_rsp {  #define NO_FILE_ID 0xFFFFFFFFFFFFFFFFULL /* general ioctls to srv not to file */ +struct compress_ioctl { +	__le16 CompressionState; /* See cifspdu.h for possible flag values */ +} __packed; +  struct smb2_ioctl_req {  	struct smb2_hdr hdr;  	__le16 StructureSize;	/* Must be 57 */ @@ -584,7 +627,7 @@ struct smb2_ioctl_req {  	__le32 MaxOutputResponse;  	__le32 Flags;  	__u32  Reserved2; -	char   Buffer[0]; +	__u8   Buffer[0];  } __packed;  struct smb2_ioctl_rsp { @@ -870,14 +913,16 @@ struct smb2_lease_ack {  /* File System Information Classes */  #define FS_VOLUME_INFORMATION		1 /* Query */ -#define FS_LABEL_INFORMATION		2 /* Set */ +#define FS_LABEL_INFORMATION		2 /* Local only */  #define FS_SIZE_INFORMATION		3 /* Query */  #define FS_DEVICE_INFORMATION		4 /* Query */  #define FS_ATTRIBUTE_INFORMATION	5 /* Query */  #define FS_CONTROL_INFORMATION		6 /* Query, Set */  #define FS_FULL_SIZE_INFORMATION	7 /* Query */  #define FS_OBJECT_ID_INFORMATION	8 /* Query, Set */ -#define FS_DRIVER_PATH_INFORMATION	9 /* Query */ +#define FS_DRIVER_PATH_INFORMATION	9 /* Local only */ +#define FS_VOLUME_FLAGS_INFORMATION	10 /* Local only */ +#define FS_SECTOR_SIZE_INFORMATION	11 /* SMB3 or later. Query */  struct smb2_fs_full_size_info {  	__le64 TotalAllocationUnits; @@ -887,6 +932,22 @@ struct smb2_fs_full_size_info {  	__le32 BytesPerSector;  } __packed; +#define SSINFO_FLAGS_ALIGNED_DEVICE		0x00000001 +#define SSINFO_FLAGS_PARTITION_ALIGNED_ON_DEVICE 0x00000002 +#define SSINFO_FLAGS_NO_SEEK_PENALTY		0x00000004 +#define SSINFO_FLAGS_TRIM_ENABLED		0x00000008 + +/* sector size info struct */ +struct smb3_fs_ss_info { +	__le32 LogicalBytesPerSector; +	__le32 PhysicalBytesPerSectorForAtomicity; +	__le32 PhysicalBytesPerSectorForPerf; +	__le32 FileSystemEffectivePhysicalBytesPerSectorForAtomicity; +	__le32 Flags; +	__le32 ByteOffsetForSectorAlignment; +	__le32 ByteOffsetForPartitionAlignment; +} __packed; +  /* partial list of QUERY INFO levels */  #define FILE_DIRECTORY_INFORMATION	1  #define FILE_FULL_DIRECTORY_INFORMATION 2 diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h index e3fb4801ee9..0ce48db20a6 100644 --- a/fs/cifs/smb2proto.h +++ b/fs/cifs/smb2proto.h @@ -61,7 +61,7 @@ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,  extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,  				struct cifs_sb_info *cifs_sb,  				const char *full_path, FILE_ALL_INFO *data, -				bool *adjust_tz); +				bool *adjust_tz, bool *symlink);  extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,  			      const char *full_path, __u64 size,  			      struct cifs_sb_info *cifs_sb, bool set_alloc); @@ -123,7 +123,8 @@ extern int SMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,  extern int smb2_async_readv(struct cifs_readdata *rdata);  extern int SMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,  		     unsigned int *nbytes, char **buf, int *buf_type); -extern int smb2_async_writev(struct cifs_writedata *wdata); +extern int smb2_async_writev(struct cifs_writedata *wdata, +			     void (*release)(struct kref *kref));  extern int SMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,  		      unsigned int *nbytes, struct kvec *iov, int n_vec);  extern int SMB2_echo(struct TCP_Server_Info *server); @@ -142,12 +143,16 @@ extern int SMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon,  extern int SMB2_set_info(const unsigned int xid, struct cifs_tcon *tcon,  			 u64 persistent_fid, u64 volatile_fid,  			 FILE_BASIC_INFO *buf); +extern int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon, +				u64 persistent_fid, u64 volatile_fid);  extern int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,  			     const u64 persistent_fid, const u64 volatile_fid,  			     const __u8 oplock_level);  extern int SMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,  			 u64 persistent_file_id, u64 volatile_file_id,  			 struct kstatfs *FSData); +extern int SMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon, +			 u64 persistent_file_id, u64 volatile_file_id, int lvl);  extern int SMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,  		     const __u64 persist_fid, const __u64 volatile_fid,  		     const __u32 pid, const __u64 length, const __u64 offset, @@ -158,5 +163,6 @@ extern int smb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,  		      struct smb2_lock_element *buf);  extern int SMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,  			    __u8 *lease_key, const __le32 lease_state); +extern int smb3_validate_negotiate(const unsigned int, struct cifs_tcon *);  #endif			/* _SMB2PROTO_H */ diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 340abca3aa5..59c748ce872 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -466,7 +466,7 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)  static inline void  smb2_seq_num_into_buf(struct TCP_Server_Info *server, struct smb2_hdr *hdr)  { -	hdr->MessageId = get_next_mid(server); +	hdr->MessageId = get_next_mid64(server);  }  static struct mid_q_entry * @@ -516,13 +516,19 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_hdr *buf,  		return -EAGAIN;  	} -	if (ses->status != CifsGood) { -		/* check if SMB2 session is bad because we are setting it up */ +	if (ses->status == CifsNew) {  		if ((buf->Command != SMB2_SESSION_SETUP) &&  		    (buf->Command != SMB2_NEGOTIATE))  			return -EAGAIN;  		/* else ok - we are setting up session */  	} + +	if (ses->status == CifsExiting) { +		if (buf->Command != SMB2_LOGOFF) +			return -EAGAIN; +		/* else ok - we are shutting down the session */ +	} +  	*mid = smb2_mid_entry_alloc(buf, ses->server);  	if (*mid == NULL)  		return -ENOMEM; diff --git a/fs/cifs/smbfsctl.h b/fs/cifs/smbfsctl.h index d952ee48f4d..0e538b5c962 100644 --- a/fs/cifs/smbfsctl.h +++ b/fs/cifs/smbfsctl.h @@ -90,16 +90,30 @@  #define FSCTL_LMR_REQUEST_RESILIENCY 0x001401D4 /* BB add struct */  #define FSCTL_LMR_GET_LINK_TRACK_INF 0x001400E8 /* BB add struct */  #define FSCTL_LMR_SET_LINK_TRACK_INF 0x001400EC /* BB add struct */ -#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204 /* BB add struct */ +#define FSCTL_VALIDATE_NEGOTIATE_INFO 0x00140204  /* Perform server-side data movement */  #define FSCTL_SRV_COPYCHUNK 0x001440F2  #define FSCTL_SRV_COPYCHUNK_WRITE 0x001480F2  #define FSCTL_QUERY_NETWORK_INTERFACE_INFO 0x001401FC /* BB add struct */  #define FSCTL_SRV_READ_HASH          0x001441BB /* BB add struct */ +/* See FSCC 2.1.2.5 */  #define IO_REPARSE_TAG_MOUNT_POINT   0xA0000003  #define IO_REPARSE_TAG_HSM           0xC0000004  #define IO_REPARSE_TAG_SIS           0x80000007 +#define IO_REPARSE_TAG_HSM2          0x80000006 +#define IO_REPARSE_TAG_DRIVER_EXTENDER 0x80000005 +/* Used by the DFS filter. See MS-DFSC */ +#define IO_REPARSE_TAG_DFS           0x8000000A +/* Used by the DFS filter See MS-DFSC */ +#define IO_REPARSE_TAG_DFSR          0x80000012 +#define IO_REPARSE_TAG_FILTER_MANAGER 0x8000000B +/* See section MS-FSCC 2.1.2.4 */ +#define IO_REPARSE_TAG_SYMLINK       0xA000000C +#define IO_REPARSE_TAG_DEDUP         0x80000013 +#define IO_REPARSE_APPXSTREAM	     0xC0000014 +/* NFS symlinks, Win 8/SMB3 and later */ +#define IO_REPARSE_TAG_NFS           0x80000014  /* fsctl flags */  /* If Flags is set to this value, the request is an FSCTL not ioctl request */ diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 6fdcb1b4a10..18cd5650a5f 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c @@ -58,7 +58,7 @@ AllocMidQEntry(const struct smb_hdr *smb_buffer, struct TCP_Server_Info *server)  		return temp;  	else {  		memset(temp, 0, sizeof(struct mid_q_entry)); -		temp->mid = smb_buffer->Mid;	/* always LE */ +		temp->mid = get_mid(smb_buffer);  		temp->pid = current->pid;  		temp->command = cpu_to_le16(smb_buffer->Command);  		cifs_dbg(FYI, "For smb_command %d\n", smb_buffer->Command); @@ -270,6 +270,26 @@ cifs_rqst_page_to_kvec(struct smb_rqst *rqst, unsigned int idx,  		iov->iov_len = rqst->rq_pagesz;  } +static unsigned long +rqst_len(struct smb_rqst *rqst) +{ +	unsigned int i; +	struct kvec *iov = rqst->rq_iov; +	unsigned long buflen = 0; + +	/* total up iov array first */ +	for (i = 0; i < rqst->rq_nvec; i++) +		buflen += iov[i].iov_len; + +	/* add in the page array if there is one */ +	if (rqst->rq_npages) { +		buflen += rqst->rq_pagesz * (rqst->rq_npages - 1); +		buflen += rqst->rq_tailsz; +	} + +	return buflen; +} +  static int  smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)  { @@ -277,6 +297,7 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)  	struct kvec *iov = rqst->rq_iov;  	int n_vec = rqst->rq_nvec;  	unsigned int smb_buf_length = get_rfc1002_length(iov[0].iov_base); +	unsigned long send_length;  	unsigned int i;  	size_t total_len = 0, sent;  	struct socket *ssocket = server->ssocket; @@ -285,6 +306,14 @@ smb_send_rqst(struct TCP_Server_Info *server, struct smb_rqst *rqst)  	if (ssocket == NULL)  		return -ENOTSOCK; +	/* sanity check send length */ +	send_length = rqst_len(rqst); +	if (send_length != smb_buf_length + 4) { +		WARN(1, "Send length mismatch(send_length=%lu smb_buf_length=%u)\n", +			send_length, smb_buf_length); +		return -EIO; +	} +  	cifs_dbg(FYI, "Sending smb: smb_len=%u\n", smb_buf_length);  	dump_smb(iov[0].iov_base, iov[0].iov_len); @@ -410,8 +439,13 @@ static int  wait_for_free_request(struct TCP_Server_Info *server, const int timeout,  		      const int optype)  { -	return wait_for_free_credits(server, timeout, -				server->ops->get_credits_field(server, optype)); +	int *val; + +	val = server->ops->get_credits_field(server, optype); +	/* Since an echo is already inflight, no need to wait to send another */ +	if (*val <= 0 && optype == CIFS_ECHO_OP) +		return -EAGAIN; +	return wait_for_free_credits(server, timeout, val);  }  static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, @@ -426,13 +460,20 @@ static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,  		return -EAGAIN;  	} -	if (ses->status != CifsGood) { -		/* check if SMB session is bad because we are setting it up */ +	if (ses->status == CifsNew) {  		if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&  			(in_buf->Command != SMB_COM_NEGOTIATE))  			return -EAGAIN;  		/* else ok - we are setting up session */  	} + +	if (ses->status == CifsExiting) { +		/* check if SMB session is bad because we are setting it up */ +		if (in_buf->Command != SMB_COM_LOGOFF_ANDX) +			return -EAGAIN; +		/* else ok - we are shutting down session */ +	} +  	*ppmidQ = AllocMidQEntry(in_buf, ses->server);  	if (*ppmidQ == NULL)  		return -ENOMEM; diff --git a/fs/cifs/xattr.c b/fs/cifs/xattr.c index 09afda4cc58..5ac836a86b1 100644 --- a/fs/cifs/xattr.c +++ b/fs/cifs/xattr.c @@ -82,9 +82,11 @@ int cifs_removexattr(struct dentry *direntry, const char *ea_name)  			goto remove_ea_exit;  		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ -		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, NULL, -			(__u16)0, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(xid, pTcon, +				full_path, ea_name, NULL, (__u16)0, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  	}  remove_ea_exit:  	kfree(full_path); @@ -149,18 +151,22 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  			cifs_dbg(FYI, "attempt to set cifs inode metadata\n");  		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ -		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, -			(__u16)value_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(xid, pTcon, +				full_path, ea_name, ea_value, (__u16)value_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN)  		   == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto set_ea_exit;  		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ -		rc = CIFSSMBSetEA(xid, pTcon, full_path, ea_name, ea_value, -			(__u16)value_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (pTcon->ses->server->ops->set_EA) +			rc = pTcon->ses->server->ops->set_EA(xid, pTcon, +				full_path, ea_name, ea_value, (__u16)value_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  	} else if (strncmp(ea_name, CIFS_XATTR_CIFS_ACL,  			strlen(CIFS_XATTR_CIFS_ACL)) == 0) {  #ifdef CONFIG_CIFS_ACL @@ -170,8 +176,12 @@ int cifs_setxattr(struct dentry *direntry, const char *ea_name,  			rc = -ENOMEM;  		} else {  			memcpy(pacl, ea_value, value_size); -			rc = set_cifs_acl(pacl, value_size, -				direntry->d_inode, full_path, CIFS_ACL_DACL); +			if (pTcon->ses->server->ops->set_acl) +				rc = pTcon->ses->server->ops->set_acl(pacl, +						value_size, direntry->d_inode, +						full_path, CIFS_ACL_DACL); +			else +				rc = -EOPNOTSUPP;  			if (rc == 0) /* force revalidate of the inode */  				CIFS_I(direntry->d_inode)->time = 0;  			kfree(pacl); @@ -272,17 +282,21 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  			/* revalidate/getattr then populate from inode */  		} /* BB add else when above is implemented */  		ea_name += XATTR_USER_PREFIX_LEN; /* skip past user. prefix */ -		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, -			buf_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (pTcon->ses->server->ops->query_all_EAs) +			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, +				full_path, ea_name, ea_value, buf_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  	} else if (strncmp(ea_name, XATTR_OS2_PREFIX, XATTR_OS2_PREFIX_LEN) == 0) {  		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_XATTR)  			goto get_ea_exit;  		ea_name += XATTR_OS2_PREFIX_LEN; /* skip past os2. prefix */ -		rc = CIFSSMBQAllEAs(xid, pTcon, full_path, ea_name, ea_value, -			buf_size, cifs_sb->local_nls, -			cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR); +		if (pTcon->ses->server->ops->query_all_EAs) +			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, +				full_path, ea_name, ea_value, buf_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  	} else if (strncmp(ea_name, POSIX_ACL_XATTR_ACCESS,  			  strlen(POSIX_ACL_XATTR_ACCESS)) == 0) {  #ifdef CONFIG_CIFS_POSIX @@ -313,8 +327,11 @@ ssize_t cifs_getxattr(struct dentry *direntry, const char *ea_name,  			u32 acllen;  			struct cifs_ntsd *pacl; -			pacl = get_cifs_acl(cifs_sb, direntry->d_inode, -						full_path, &acllen); +			if (pTcon->ses->server->ops->get_acl == NULL) +				goto get_ea_exit; /* rc already EOPNOTSUPP */ + +			pacl = pTcon->ses->server->ops->get_acl(cifs_sb, +					direntry->d_inode, full_path, &acllen);  			if (IS_ERR(pacl)) {  				rc = PTR_ERR(pacl);  				cifs_dbg(VFS, "%s: error %zd getting sec desc\n", @@ -400,11 +417,12 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)  	/* if proc/fs/cifs/streamstoxattr is set then  		search server for EAs or streams to  		returns as xattrs */ -	rc = CIFSSMBQAllEAs(xid, pTcon, full_path, NULL, data, -				buf_size, cifs_sb->local_nls, -				cifs_sb->mnt_cifs_flags & -					CIFS_MOUNT_MAP_SPECIAL_CHR); +	if (pTcon->ses->server->ops->query_all_EAs) +		rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon, +				full_path, NULL, data, buf_size, +				cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & +					CIFS_MOUNT_MAP_SPECIAL_CHR);  list_ea_exit:  	kfree(full_path);  	free_xid(xid);  | 
