Commit 1213959d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.samba.org/sfrench/cifs-2.6

Pull CIFS fixes from Steve French:
 "A set of cifs fixes most important of which is Pavel's fix for some
  problems with handling Windows reparse points and also the security
  fix for setfacl over a cifs mount to Samba removing part of the ACL.
  Both of these fixes are for stable as well.

  Also added most of copychunk (copy offload) support to cifs although I
  expect a final patch in that series (to fix handling of larger files)
  in a few days (had to hold off on that in order to incorporate some
  additional code review feedback).

  Also added support for O_DIRECT on forcedirectio mounts (needed in
  order to run some of the server benchmarks over cifs and smb2/smb3
  mounts)"

* 'for-linus' of git://git.samba.org/sfrench/cifs-2.6:
  [CIFS] Warn if SMB3 encryption required by server
  setfacl removes part of ACL when setting POSIX ACLs to Samba
  [CIFS] Set copychunk defaults
  CIFS: SMB2/SMB3 Copy offload support (refcopy) phase 1
  cifs: Use data structures to compute NTLMv2 response offsets
  [CIFS] O_DIRECT opens should work on directio mounts
  cifs: don't spam the logs on unexpected lookup errors
  cifs: change ERRnomem error mapping from ENOMEM to EREMOTEIO
  CIFS: Fix symbolic links usage
parents 673fdfe3 0cbaa53c
...@@ -548,7 +548,13 @@ static int ...@@ -548,7 +548,13 @@ static int
CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
{ {
int rc; 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) { if (!ses->server->secmech.sdeschmacmd5) {
cifs_dbg(VFS, "%s: can't generate ntlmv2 hash\n", __func__); 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) ...@@ -556,7 +562,7 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
} }
rc = crypto_shash_setkey(ses->server->secmech.hmacmd5, rc = crypto_shash_setkey(ses->server->secmech.hmacmd5,
ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE); ntlmv2_hash, CIFS_HMAC_MD5_HASH_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n", cifs_dbg(VFS, "%s: Could not set NTLMV2 Hash as a key\n",
__func__); __func__);
...@@ -570,20 +576,21 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) ...@@ -570,20 +576,21 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
} }
if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED) if (ses->server->negflavor == CIFS_NEGFLAVOR_EXTENDED)
memcpy(ses->auth_key.response + offset, memcpy(ntlmv2->challenge.key,
ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); ses->ntlmssp->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
else else
memcpy(ses->auth_key.response + offset, memcpy(ntlmv2->challenge.key,
ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE); ses->server->cryptkey, CIFS_SERVER_CHALLENGE_SIZE);
rc = crypto_shash_update(&ses->server->secmech.sdeschmacmd5->shash, 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) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
return rc; return rc;
} }
/* Note that the MD5 digest over writes anon.challenge_key.key */
rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash, rc = crypto_shash_final(&ses->server->secmech.sdeschmacmd5->shash,
ses->auth_key.response + CIFS_SESS_KEY_SIZE); ntlmv2->ntlmv2_hash);
if (rc) if (rc)
cifs_dbg(VFS, "%s: Could not generate md5 hash\n", __func__); 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) ...@@ -627,7 +634,7 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
int rc; int rc;
int baselen; int baselen;
unsigned int tilen; unsigned int tilen;
struct ntlmv2_resp *buf; struct ntlmv2_resp *ntlmv2;
char ntlmv2_hash[16]; char ntlmv2_hash[16];
unsigned char *tiblob = NULL; /* target info blob */ unsigned char *tiblob = NULL; /* target info blob */
...@@ -660,13 +667,14 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -660,13 +667,14 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
} }
ses->auth_key.len += baselen; ses->auth_key.len += baselen;
buf = (struct ntlmv2_resp *) ntlmv2 = (struct ntlmv2_resp *)
(ses->auth_key.response + CIFS_SESS_KEY_SIZE); (ses->auth_key.response + CIFS_SESS_KEY_SIZE);
buf->blob_signature = cpu_to_le32(0x00000101); ntlmv2->blob_signature = cpu_to_le32(0x00000101);
buf->reserved = 0; ntlmv2->reserved = 0;
buf->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME)); /* Must be within 5 minutes of the server */
get_random_bytes(&buf->client_chal, sizeof(buf->client_chal)); ntlmv2->time = cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
buf->reserved2 = 0; get_random_bytes(&ntlmv2->client_chal, sizeof(ntlmv2->client_chal));
ntlmv2->reserved2 = 0;
memcpy(ses->auth_key.response + baselen, tiblob, tilen); 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) ...@@ -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, 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); CIFS_HMAC_MD5_HASH_SIZE);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not update with response\n", __func__); cifs_dbg(VFS, "%s: Could not update with response\n", __func__);
......
...@@ -261,7 +261,7 @@ struct smb_version_operations { ...@@ -261,7 +261,7 @@ struct smb_version_operations {
/* query path data from the server */ /* query path data from the server */
int (*query_path_info)(const unsigned int, struct cifs_tcon *, int (*query_path_info)(const unsigned int, struct cifs_tcon *,
struct cifs_sb_info *, const char *, struct cifs_sb_info *, const char *,
FILE_ALL_INFO *, bool *); FILE_ALL_INFO *, bool *, bool *);
/* query file data from the server */ /* query file data from the server */
int (*query_file_info)(const unsigned int, struct cifs_tcon *, int (*query_file_info)(const unsigned int, struct cifs_tcon *,
struct cifs_fid *, FILE_ALL_INFO *); struct cifs_fid *, FILE_ALL_INFO *);
...@@ -381,6 +381,9 @@ struct smb_version_operations { ...@@ -381,6 +381,9 @@ struct smb_version_operations {
char * (*create_lease_buf)(u8 *, u8); char * (*create_lease_buf)(u8 *, u8);
/* parse lease context buffer and return oplock/epoch info */ /* parse lease context buffer and return oplock/epoch info */
__u8 (*parse_lease_buf)(void *, unsigned int *); __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);
}; };
struct smb_version_values { struct smb_version_values {
...@@ -855,6 +858,9 @@ struct cifs_tcon { ...@@ -855,6 +858,9 @@ struct cifs_tcon {
__le64 vol_create_time; __le64 vol_create_time;
__u32 ss_flags; /* sector size flags */ __u32 ss_flags; /* sector size flags */
__u32 perf_sector_size; /* best sector size for perf */ __u32 perf_sector_size; /* best sector size for perf */
__u32 max_chunks;
__u32 max_bytes_chunk;
__u32 max_bytes_copy;
#endif /* CONFIG_CIFS_SMB2 */ #endif /* CONFIG_CIFS_SMB2 */
#ifdef CONFIG_CIFS_FSCACHE #ifdef CONFIG_CIFS_FSCACHE
u64 resource_id; /* server resource id */ u64 resource_id; /* server resource id */
......
...@@ -697,7 +697,13 @@ struct ntlmssp2_name { ...@@ -697,7 +697,13 @@ struct ntlmssp2_name {
} __attribute__((packed)); } __attribute__((packed));
struct ntlmv2_resp { 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; __le32 blob_signature;
__u32 reserved; __u32 reserved;
__le64 time; __le64 time;
......
...@@ -3369,11 +3369,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL, ...@@ -3369,11 +3369,13 @@ static __u16 ACL_to_cifs_posix(char *parm_data, const char *pACL,
return 0; return 0;
} }
cifs_acl->version = cpu_to_le16(1); 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); 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); 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); cifs_dbg(FYI, "unknown ACL type %d\n", acl_type);
return 0; return 0;
} }
......
...@@ -756,7 +756,7 @@ cifs_lookup(struct inode *parent_dir_inode, struct dentry *direntry, ...@@ -756,7 +756,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 /* if it was once a directory (but how can we tell?) we could do
shrink_dcache_parent(direntry); */ shrink_dcache_parent(direntry); */
} else if (rc != -EACCES) { } 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 /* We special case check for Access Denied - since that
is a common return code */ is a common return code */
} }
......
...@@ -3663,6 +3663,27 @@ void cifs_oplock_break(struct work_struct *work) ...@@ -3663,6 +3663,27 @@ void cifs_oplock_break(struct work_struct *work)
} }
} }
/*
* 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, const struct iovec *iov,
loff_t pos, unsigned long nr_segs)
{
/*
* FIXME
* Eventually need to support direct IO for non forcedirectio mounts
*/
return -EINVAL;
}
const struct address_space_operations cifs_addr_ops = { const struct address_space_operations cifs_addr_ops = {
.readpage = cifs_readpage, .readpage = cifs_readpage,
.readpages = cifs_readpages, .readpages = cifs_readpages,
...@@ -3672,6 +3693,7 @@ const struct address_space_operations cifs_addr_ops = { ...@@ -3672,6 +3693,7 @@ const struct address_space_operations cifs_addr_ops = {
.write_end = cifs_write_end, .write_end = cifs_write_end,
.set_page_dirty = __set_page_dirty_nobuffers, .set_page_dirty = __set_page_dirty_nobuffers,
.releasepage = cifs_release_page, .releasepage = cifs_release_page,
.direct_IO = cifs_direct_io,
.invalidatepage = cifs_invalidate_page, .invalidatepage = cifs_invalidate_page,
.launder_page = cifs_launder_page, .launder_page = cifs_launder_page,
}; };
......
...@@ -542,7 +542,8 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path, ...@@ -542,7 +542,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 */ /* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
static void static void
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
struct cifs_sb_info *cifs_sb, bool adjust_tz) struct cifs_sb_info *cifs_sb, bool adjust_tz,
bool symlink)
{ {
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb); struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
...@@ -569,7 +570,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, ...@@ -569,7 +570,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
fattr->cf_createtime = le64_to_cpu(info->CreationTime); fattr->cf_createtime = le64_to_cpu(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
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_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
fattr->cf_dtype = DT_DIR; fattr->cf_dtype = DT_DIR;
/* /*
...@@ -578,10 +583,6 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, ...@@ -578,10 +583,6 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
*/ */
if (!tcon->unix_ext) if (!tcon->unix_ext)
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
} 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);
} else { } else {
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG; fattr->cf_dtype = DT_REG;
...@@ -626,7 +627,8 @@ cifs_get_file_info(struct file *filp) ...@@ -626,7 +627,8 @@ cifs_get_file_info(struct file *filp)
rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data); rc = server->ops->query_file_info(xid, tcon, &cfile->fid, &find_data);
switch (rc) { switch (rc) {
case 0: 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; break;
case -EREMOTE: case -EREMOTE:
cifs_create_dfs_fattr(&fattr, inode->i_sb); cifs_create_dfs_fattr(&fattr, inode->i_sb);
...@@ -673,6 +675,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -673,6 +675,7 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
bool adjust_tz = false; bool adjust_tz = false;
struct cifs_fattr fattr; struct cifs_fattr fattr;
struct cifs_search_info *srchinf = NULL; struct cifs_search_info *srchinf = NULL;
bool symlink = false;
tlink = cifs_sb_tlink(cifs_sb); tlink = cifs_sb_tlink(cifs_sb);
if (IS_ERR(tlink)) if (IS_ERR(tlink))
...@@ -702,12 +705,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path, ...@@ -702,12 +705,12 @@ cifs_get_inode_info(struct inode **inode, const char *full_path,
} }
data = (FILE_ALL_INFO *)buf; data = (FILE_ALL_INFO *)buf;
rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path,
data, &adjust_tz); data, &adjust_tz, &symlink);
} }
if (!rc) { if (!rc) {
cifs_all_info_to_fattr(&fattr, (FILE_ALL_INFO *)data, cifs_sb, cifs_all_info_to_fattr(&fattr, data, cifs_sb, adjust_tz,
adjust_tz); symlink);
} else if (rc == -EREMOTE) { } else if (rc == -EREMOTE) {
cifs_create_dfs_fattr(&fattr, sb); cifs_create_dfs_fattr(&fattr, sb);
rc = 0; rc = 0;
......
...@@ -22,12 +22,120 @@ ...@@ -22,12 +22,120 @@
*/ */
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/file.h>
#include <linux/mount.h>
#include <linux/mm.h>
#include <linux/pagemap.h>
#include <linux/btrfs.h>
#include "cifspdu.h" #include "cifspdu.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifsfs.h" #include "cifsfs.h"
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 = src_file.file->f_dentry->d_inode;
/*
* 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) long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
{ {
struct inode *inode = file_inode(filep); struct inode *inode = file_inode(filep);
...@@ -105,6 +213,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) ...@@ -105,6 +213,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
cifs_dbg(FYI, "set compress flag rc %d\n", rc); cifs_dbg(FYI, "set compress flag rc %d\n", rc);
} }
break; break;
case BTRFS_IOC_CLONE:
rc = cifs_ioctl_clone(xid, filep, arg, 0, 0, 0);
break;
default: default:
cifs_dbg(FYI, "unsupported ioctl\n"); cifs_dbg(FYI, "unsupported ioctl\n");
break; break;
......
...@@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = { ...@@ -51,7 +51,7 @@ static const struct smb_to_posix_error mapping_table_ERRDOS[] = {
{ERRnoaccess, -EACCES}, {ERRnoaccess, -EACCES},
{ERRbadfid, -EBADF}, {ERRbadfid, -EBADF},
{ERRbadmcb, -EIO}, {ERRbadmcb, -EIO},
{ERRnomem, -ENOMEM}, {ERRnomem, -EREMOTEIO},
{ERRbadmem, -EFAULT}, {ERRbadmem, -EFAULT},
{ERRbadenv, -EFAULT}, {ERRbadenv, -EFAULT},
{ERRbadformat, -EINVAL}, {ERRbadformat, -EINVAL},
......
...@@ -134,22 +134,6 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name, ...@@ -134,22 +134,6 @@ cifs_prime_dcache(struct dentry *parent, struct qstr *name,
dput(dentry); 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 static void
cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
{ {
...@@ -159,27 +143,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) ...@@ -159,27 +143,19 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) { if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode; fattr->cf_mode = S_IFDIR | cifs_sb->mnt_dir_mode;
fattr->cf_dtype = DT_DIR; 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 { } else {
fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode; fattr->cf_mode = S_IFREG | cifs_sb->mnt_file_mode;
fattr->cf_dtype = DT_REG; 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 */ /* non-unix readdir doesn't provide nlink */
fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK; fattr->cf_flags |= CIFS_FATTR_UNKNOWN_NLINK;
......
...@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -534,10 +534,12 @@ cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
static int static int
cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, 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; int rc;
*symlink = false;
/* could do find first instead but this returns more info */ /* could do find first instead but this returns more info */
rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
...@@ -554,6 +556,23 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -554,6 +556,23 @@ cifs_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
*adjustTZ = true; *adjustTZ = true;
} }
if (!rc && (le32_to_cpu(data->Attributes) & ATTR_REPARSE)) {
int tmprc;
int oplock = 0;
__u16 netfid;
/* Need to check if this is a symbolic link or not */
tmprc = CIFSSMBOpen(xid, tcon, full_path, FILE_OPEN,
FILE_READ_ATTRIBUTES, 0, &netfid, &oplock,
NULL, cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR);
if (tmprc == -EOPNOTSUPP)
*symlink = true;
else
CIFSSMBClose(xid, tcon, netfid);
}
return rc; return rc;
} }
......
...@@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src) ...@@ -123,12 +123,13 @@ move_smb2_info_to_cifs(FILE_ALL_INFO *dst, struct smb2_file_all_info *src)
int int
smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path, 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; int rc;
struct smb2_file_all_info *smb2_data; struct smb2_file_all_info *smb2_data;
*adjust_tz = false; *adjust_tz = false;
*symlink = false;
smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2, smb2_data = kzalloc(sizeof(struct smb2_file_all_info) + MAX_NAME * 2,
GFP_KERNEL); GFP_KERNEL);
...@@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -136,9 +137,16 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
return -ENOMEM; return -ENOMEM;
rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path, rc = smb2_open_op_close(xid, tcon, cifs_sb, full_path,
FILE_READ_ATTRIBUTES, FILE_OPEN, FILE_READ_ATTRIBUTES, FILE_OPEN, 0,
OPEN_REPARSE_POINT, smb2_data, smb2_data, SMB2_OP_QUERY_INFO);
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) if (rc)
goto out; goto out;
......
...@@ -306,7 +306,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = { ...@@ -306,7 +306,7 @@ static const struct status_to_posix_error smb2_error_map_table[] = {
{STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"}, {STATUS_NONEXISTENT_SECTOR, -EIO, "STATUS_NONEXISTENT_SECTOR"},
{STATUS_MORE_PROCESSING_REQUIRED, -EIO, {STATUS_MORE_PROCESSING_REQUIRED, -EIO,
"STATUS_MORE_PROCESSING_REQUIRED"}, "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, -EADDRINUSE,
"STATUS_CONFLICTING_ADDRESSES"}, "STATUS_CONFLICTING_ADDRESSES"},
{STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"}, {STATUS_NOT_MAPPED_VIEW, -EIO, "STATUS_NOT_MAPPED_VIEW"},
......
...@@ -493,6 +493,85 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -493,6 +493,85 @@ smb2_close_file(const unsigned int xid, struct cifs_tcon *tcon,
SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid); SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
} }
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;
char *retbuf = NULL;
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)
return rc;
/* For now array only one chunk long, will make more flexible later */
pcchunk->ChunkCount = __constant_cpu_to_le32(1);
pcchunk->Reserved = 0;
pcchunk->SourceOffset = cpu_to_le64(src_off);
pcchunk->TargetOffset = cpu_to_le64(dest_off);
pcchunk->Length = cpu_to_le32(len);
pcchunk->Reserved2 = 0;
/* Request that server copy to target from src file identified by key */
rc = SMB2_ioctl(xid, tlink_tcon(trgtfile->tlink),
trgtfile->fid.persistent_fid,
trgtfile->fid.volatile_fid, FSCTL_SRV_COPYCHUNK_WRITE,
true /* is_fsctl */, (char *)pcchunk,
sizeof(struct copychunk_ioctl), &retbuf, &ret_data_len);
/* BB need to special case rc = EINVAL to alter chunk size */
cifs_dbg(FYI, "rc %d data length out %d\n", rc, ret_data_len);
kfree(pcchunk);
return rc;
}
static int static int
smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon, smb2_flush_file(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *fid) struct cifs_fid *fid)
...@@ -1017,6 +1096,7 @@ struct smb_version_operations smb20_operations = { ...@@ -1017,6 +1096,7 @@ struct smb_version_operations smb20_operations = {
.set_oplock_level = smb2_set_oplock_level, .set_oplock_level = smb2_set_oplock_level,
.create_lease_buf = smb2_create_lease_buf, .create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf, .parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range,
}; };
struct smb_version_operations smb21_operations = { struct smb_version_operations smb21_operations = {
...@@ -1090,6 +1170,7 @@ struct smb_version_operations smb21_operations = { ...@@ -1090,6 +1170,7 @@ struct smb_version_operations smb21_operations = {
.set_oplock_level = smb21_set_oplock_level, .set_oplock_level = smb21_set_oplock_level,
.create_lease_buf = smb2_create_lease_buf, .create_lease_buf = smb2_create_lease_buf,
.parse_lease_buf = smb2_parse_lease_buf, .parse_lease_buf = smb2_parse_lease_buf,
.clone_range = smb2_clone_range,
}; };
struct smb_version_operations smb30_operations = { struct smb_version_operations smb30_operations = {
...@@ -1165,6 +1246,7 @@ struct smb_version_operations smb30_operations = { ...@@ -1165,6 +1246,7 @@ struct smb_version_operations smb30_operations = {
.set_oplock_level = smb3_set_oplock_level, .set_oplock_level = smb3_set_oplock_level,
.create_lease_buf = smb3_create_lease_buf, .create_lease_buf = smb3_create_lease_buf,
.parse_lease_buf = smb3_parse_lease_buf, .parse_lease_buf = smb3_parse_lease_buf,
.clone_range = smb2_clone_range,
}; };
struct smb_version_values smb20_values = { struct smb_version_values smb20_values = {
......
...@@ -630,6 +630,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -630,6 +630,8 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
goto ssetup_exit; goto ssetup_exit;
ses->session_flags = le16_to_cpu(rsp->SessionFlags); 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: ssetup_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
...@@ -717,6 +719,14 @@ static inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code) ...@@ -717,6 +719,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 */) #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 int
SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
struct cifs_tcon *tcon, const struct nls_table *cp) struct cifs_tcon *tcon, const struct nls_table *cp)
...@@ -818,7 +828,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -818,7 +828,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) && if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
((tcon->share_flags & SHI1005_FLAGS_DFS) == 0)) ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
cifs_dbg(VFS, "DFS capability contradicts DFS flag\n"); cifs_dbg(VFS, "DFS capability contradicts DFS flag\n");
init_copy_chunk_defaults(tcon);
tcon_exit: tcon_exit:
free_rsp_buf(resp_buftype, rsp); free_rsp_buf(resp_buftype, rsp);
kfree(unc_path); kfree(unc_path);
......
...@@ -122,6 +122,23 @@ struct smb2_pdu { ...@@ -122,6 +122,23 @@ struct smb2_pdu {
__le16 StructureSize2; /* size of wct area (varies, request specific) */ __le16 StructureSize2; /* size of wct area (varies, request specific) */
} __packed; } __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 * SMB2 flag definitions
*/ */
...@@ -237,6 +254,7 @@ struct smb2_sess_setup_req { ...@@ -237,6 +254,7 @@ struct smb2_sess_setup_req {
/* Currently defined SessionFlags */ /* Currently defined SessionFlags */
#define SMB2_SESSION_FLAG_IS_GUEST 0x0001 #define SMB2_SESSION_FLAG_IS_GUEST 0x0001
#define SMB2_SESSION_FLAG_IS_NULL 0x0002 #define SMB2_SESSION_FLAG_IS_NULL 0x0002
#define SMB2_SESSION_FLAG_ENCRYPT_DATA 0x0004
struct smb2_sess_setup_rsp { struct smb2_sess_setup_rsp {
struct smb2_hdr hdr; struct smb2_hdr hdr;
__le16 StructureSize; /* Must be 9 */ __le16 StructureSize; /* Must be 9 */
...@@ -534,9 +552,16 @@ struct create_durable { ...@@ -534,9 +552,16 @@ struct create_durable {
} Data; } Data;
} __packed; } __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 */ /* this goes in the ioctl buffer when doing a copychunk request */
struct copychunk_ioctl { struct copychunk_ioctl {
char SourceKey[24]; char SourceKey[COPY_CHUNK_RES_KEY_SIZE];
__le32 ChunkCount; /* we are only sending 1 */ __le32 ChunkCount; /* we are only sending 1 */
__le32 Reserved; __le32 Reserved;
/* array will only be one chunk long for us */ /* array will only be one chunk long for us */
...@@ -546,6 +571,12 @@ struct copychunk_ioctl { ...@@ -546,6 +571,12 @@ struct copychunk_ioctl {
__u32 Reserved2; __u32 Reserved2;
} __packed; } __packed;
struct copychunk_ioctl_rsp {
__le32 ChunksWritten;
__le32 ChunkBytesWritten;
__le32 TotalBytesWritten;
} __packed;
/* Response and Request are the same format */ /* Response and Request are the same format */
struct validate_negotiate_info { struct validate_negotiate_info {
__le32 Capabilities; __le32 Capabilities;
......
...@@ -61,7 +61,7 @@ extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, ...@@ -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, extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, struct cifs_sb_info *cifs_sb,
const char *full_path, FILE_ALL_INFO *data, 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, extern int smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
const char *full_path, __u64 size, const char *full_path, __u64 size,
struct cifs_sb_info *cifs_sb, bool set_alloc); struct cifs_sb_info *cifs_sb, bool set_alloc);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment