Commit f1c41088 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:
 "Fixes for 4 cifs bugs, including a reconnect problem, a problem
  parsing responses to SMB2 open request, and setting nlink incorrectly
  to some servers which don't report it properly on the wire.  Also
  improves data integrity on reconnect with series from Pavel which adds
  durable handle support for SMB2."

* 'for-linus' of git://git.samba.org/sfrench/cifs-2.6:
  CIFS: Fix a deadlock when a file is reopened
  CIFS: Reopen the file if reconnect durable handle failed
  [CIFS] Fix minor endian error in durable handle patch series
  CIFS: Reconnect durable handles for SMB2
  CIFS: Make SMB2_open use cifs_open_parms struct
  CIFS: Introduce cifs_open_parms struct
  CIFS: Request durable open for SMB2 opens
  CIFS: Simplify SMB2 create context handling
  CIFS: Simplify SMB2_open code path
  CIFS: Respect create_options in smb2_open_file
  CIFS: Fix lease context buffer parsing
  [CIFS] use sensible file nlink values if unprovided
  Limit allocation of crypto mechanisms to dialect which requires
parents 9903883f 689c3db4
/* /*
* fs/cifs/cifsencrypt.c * fs/cifs/cifsencrypt.c
* *
* Copyright (C) International Business Machines Corp., 2005,2006 * Copyright (C) International Business Machines Corp., 2005,2013
* Author(s): Steve French (sfrench@us.ibm.com) * Author(s): Steve French (sfrench@us.ibm.com)
* *
* This library is free software; you can redistribute it and/or modify * This library is free software; you can redistribute it and/or modify
...@@ -31,6 +31,36 @@ ...@@ -31,6 +31,36 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/highmem.h> #include <linux/highmem.h>
static int
cifs_crypto_shash_md5_allocate(struct TCP_Server_Info *server)
{
int rc;
unsigned int size;
if (server->secmech.sdescmd5 != NULL)
return 0; /* already allocated */
server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
if (IS_ERR(server->secmech.md5)) {
cifs_dbg(VFS, "could not allocate crypto md5\n");
return PTR_ERR(server->secmech.md5);
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.md5);
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdescmd5) {
rc = -ENOMEM;
crypto_free_shash(server->secmech.md5);
server->secmech.md5 = NULL;
return rc;
}
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
server->secmech.sdescmd5->shash.flags = 0x0;
return 0;
}
/* /*
* Calculate and return the CIFS signature based on the mac key and SMB PDU. * Calculate and return the CIFS signature based on the mac key and SMB PDU.
* The 16 byte signature must be allocated by the caller. Note we only use the * The 16 byte signature must be allocated by the caller. Note we only use the
...@@ -50,9 +80,12 @@ static int cifs_calc_signature(struct smb_rqst *rqst, ...@@ -50,9 +80,12 @@ static int cifs_calc_signature(struct smb_rqst *rqst,
return -EINVAL; return -EINVAL;
if (!server->secmech.sdescmd5) { if (!server->secmech.sdescmd5) {
cifs_dbg(VFS, "%s: Can't generate signature\n", __func__); rc = cifs_crypto_shash_md5_allocate(server);
if (rc) {
cifs_dbg(VFS, "%s: Can't alloc md5 crypto\n", __func__);
return -1; return -1;
} }
}
rc = crypto_shash_init(&server->secmech.sdescmd5->shash); rc = crypto_shash_init(&server->secmech.sdescmd5->shash);
if (rc) { if (rc) {
...@@ -556,6 +589,33 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash) ...@@ -556,6 +589,33 @@ CalcNTLMv2_response(const struct cifs_ses *ses, char *ntlmv2_hash)
return rc; return rc;
} }
static int crypto_hmacmd5_alloc(struct TCP_Server_Info *server)
{
unsigned int size;
/* check if already allocated */
if (server->secmech.sdeschmacmd5)
return 0;
server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
if (IS_ERR(server->secmech.hmacmd5)) {
cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
return PTR_ERR(server->secmech.hmacmd5);
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacmd5);
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacmd5) {
crypto_free_shash(server->secmech.hmacmd5);
server->secmech.hmacmd5 = NULL;
return -ENOMEM;
}
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
server->secmech.sdeschmacmd5->shash.flags = 0x0;
return 0;
}
int int
setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
...@@ -606,6 +666,12 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp) ...@@ -606,6 +666,12 @@ setup_ntlmv2_rsp(struct cifs_ses *ses, const struct nls_table *nls_cp)
memcpy(ses->auth_key.response + baselen, tiblob, tilen); memcpy(ses->auth_key.response + baselen, tiblob, tilen);
rc = crypto_hmacmd5_alloc(ses->server);
if (rc) {
cifs_dbg(VFS, "could not crypto alloc hmacmd5 rc %d\n", rc);
goto setup_ntlmv2_rsp_ret;
}
/* calculate ntlmv2_hash */ /* calculate ntlmv2_hash */
rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp); rc = calc_ntlmv2_hash(ses, ntlmv2_hash, nls_cp);
if (rc) { if (rc) {
...@@ -705,123 +771,32 @@ calc_seckey(struct cifs_ses *ses) ...@@ -705,123 +771,32 @@ calc_seckey(struct cifs_ses *ses)
void void
cifs_crypto_shash_release(struct TCP_Server_Info *server) cifs_crypto_shash_release(struct TCP_Server_Info *server)
{ {
if (server->secmech.cmacaes) if (server->secmech.cmacaes) {
crypto_free_shash(server->secmech.cmacaes); crypto_free_shash(server->secmech.cmacaes);
server->secmech.cmacaes = NULL;
}
if (server->secmech.hmacsha256) if (server->secmech.hmacsha256) {
crypto_free_shash(server->secmech.hmacsha256); crypto_free_shash(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
}
if (server->secmech.md5) if (server->secmech.md5) {
crypto_free_shash(server->secmech.md5); crypto_free_shash(server->secmech.md5);
server->secmech.md5 = NULL;
}
if (server->secmech.hmacmd5) if (server->secmech.hmacmd5) {
crypto_free_shash(server->secmech.hmacmd5); crypto_free_shash(server->secmech.hmacmd5);
server->secmech.hmacmd5 = NULL;
}
kfree(server->secmech.sdesccmacaes); kfree(server->secmech.sdesccmacaes);
server->secmech.sdesccmacaes = NULL;
kfree(server->secmech.sdeschmacsha256); kfree(server->secmech.sdeschmacsha256);
server->secmech.sdeschmacsha256 = NULL;
kfree(server->secmech.sdeschmacmd5); kfree(server->secmech.sdeschmacmd5);
server->secmech.sdeschmacmd5 = NULL;
kfree(server->secmech.sdescmd5);
}
int
cifs_crypto_shash_allocate(struct TCP_Server_Info *server)
{
int rc;
unsigned int size;
server->secmech.hmacmd5 = crypto_alloc_shash("hmac(md5)", 0, 0);
if (IS_ERR(server->secmech.hmacmd5)) {
cifs_dbg(VFS, "could not allocate crypto hmacmd5\n");
return PTR_ERR(server->secmech.hmacmd5);
}
server->secmech.md5 = crypto_alloc_shash("md5", 0, 0);
if (IS_ERR(server->secmech.md5)) {
cifs_dbg(VFS, "could not allocate crypto md5\n");
rc = PTR_ERR(server->secmech.md5);
goto crypto_allocate_md5_fail;
}
server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
if (IS_ERR(server->secmech.hmacsha256)) {
cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
rc = PTR_ERR(server->secmech.hmacsha256);
goto crypto_allocate_hmacsha256_fail;
}
server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
if (IS_ERR(server->secmech.cmacaes)) {
cifs_dbg(VFS, "could not allocate crypto cmac-aes");
rc = PTR_ERR(server->secmech.cmacaes);
goto crypto_allocate_cmacaes_fail;
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacmd5);
server->secmech.sdeschmacmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacmd5) {
rc = -ENOMEM;
goto crypto_allocate_hmacmd5_sdesc_fail;
}
server->secmech.sdeschmacmd5->shash.tfm = server->secmech.hmacmd5;
server->secmech.sdeschmacmd5->shash.flags = 0x0;
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.md5);
server->secmech.sdescmd5 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdescmd5) {
rc = -ENOMEM;
goto crypto_allocate_md5_sdesc_fail;
}
server->secmech.sdescmd5->shash.tfm = server->secmech.md5;
server->secmech.sdescmd5->shash.flags = 0x0;
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacsha256);
server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacsha256) {
rc = -ENOMEM;
goto crypto_allocate_hmacsha256_sdesc_fail;
}
server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
server->secmech.sdeschmacsha256->shash.flags = 0x0;
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.cmacaes);
server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdesccmacaes) {
cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
rc = -ENOMEM;
goto crypto_allocate_cmacaes_sdesc_fail;
}
server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
server->secmech.sdesccmacaes->shash.flags = 0x0;
return 0;
crypto_allocate_cmacaes_sdesc_fail:
kfree(server->secmech.sdeschmacsha256);
crypto_allocate_hmacsha256_sdesc_fail:
kfree(server->secmech.sdescmd5); kfree(server->secmech.sdescmd5);
server->secmech.sdescmd5 = NULL;
crypto_allocate_md5_sdesc_fail:
kfree(server->secmech.sdeschmacmd5);
crypto_allocate_hmacmd5_sdesc_fail:
crypto_free_shash(server->secmech.cmacaes);
crypto_allocate_cmacaes_fail:
crypto_free_shash(server->secmech.hmacsha256);
crypto_allocate_hmacsha256_fail:
crypto_free_shash(server->secmech.md5);
crypto_allocate_md5_fail:
crypto_free_shash(server->secmech.hmacmd5);
return rc;
} }
...@@ -194,6 +194,7 @@ struct cifs_writedata; ...@@ -194,6 +194,7 @@ struct cifs_writedata;
struct cifs_io_parms; struct cifs_io_parms;
struct cifs_search_info; struct cifs_search_info;
struct cifsInodeInfo; struct cifsInodeInfo;
struct cifs_open_parms;
struct smb_version_operations { struct smb_version_operations {
int (*send_cancel)(struct TCP_Server_Info *, void *, int (*send_cancel)(struct TCP_Server_Info *, void *,
...@@ -307,9 +308,8 @@ struct smb_version_operations { ...@@ -307,9 +308,8 @@ struct smb_version_operations {
const char *, const char *, const char *, const char *,
struct cifs_sb_info *); struct cifs_sb_info *);
/* open a file for non-posix mounts */ /* open a file for non-posix mounts */
int (*open)(const unsigned int, struct cifs_tcon *, const char *, int, int (*open)(const unsigned int, struct cifs_open_parms *,
int, int, struct cifs_fid *, __u32 *, FILE_ALL_INFO *, __u32 *, FILE_ALL_INFO *);
struct cifs_sb_info *);
/* set fid protocol-specific info */ /* set fid protocol-specific info */
void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32); void (*set_fid)(struct cifsFileInfo *, struct cifs_fid *, __u32);
/* close a file */ /* close a file */
...@@ -912,6 +912,17 @@ struct cifs_search_info { ...@@ -912,6 +912,17 @@ struct cifs_search_info {
bool smallBuf:1; /* so we know which buf_release function to call */ bool smallBuf:1; /* so we know which buf_release function to call */
}; };
struct cifs_open_parms {
struct cifs_tcon *tcon;
struct cifs_sb_info *cifs_sb;
int disposition;
int desired_access;
int create_options;
const char *path;
struct cifs_fid *fid;
bool reconnect:1;
};
struct cifs_fid { struct cifs_fid {
__u16 netfid; __u16 netfid;
#ifdef CONFIG_CIFS_SMB2 #ifdef CONFIG_CIFS_SMB2
......
...@@ -433,7 +433,6 @@ extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *, ...@@ -433,7 +433,6 @@ extern int SMBNTencrypt(unsigned char *, unsigned char *, unsigned char *,
const struct nls_table *); const struct nls_table *);
extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *); extern int setup_ntlm_response(struct cifs_ses *, const struct nls_table *);
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern int cifs_crypto_shash_allocate(struct TCP_Server_Info *);
extern void cifs_crypto_shash_release(struct TCP_Server_Info *); extern void cifs_crypto_shash_release(struct TCP_Server_Info *);
extern int calc_seckey(struct cifs_ses *); extern int calc_seckey(struct cifs_ses *);
extern void generate_smb3signingkey(struct TCP_Server_Info *); extern void generate_smb3signingkey(struct TCP_Server_Info *);
......
...@@ -2108,12 +2108,6 @@ cifs_get_tcp_session(struct smb_vol *volume_info) ...@@ -2108,12 +2108,6 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
goto out_err; goto out_err;
} }
rc = cifs_crypto_shash_allocate(tcp_ses);
if (rc) {
cifs_dbg(VFS, "could not setup hash structures rc %d\n", rc);
goto out_err;
}
tcp_ses->ops = volume_info->ops; tcp_ses->ops = volume_info->ops;
tcp_ses->vals = volume_info->vals; tcp_ses->vals = volume_info->vals;
cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns)); cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
......
...@@ -204,6 +204,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -204,6 +204,7 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
struct inode *newinode = NULL; struct inode *newinode = NULL;
int disposition; int disposition;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
*oplock = 0; *oplock = 0;
if (tcon->ses->server->oplocks) if (tcon->ses->server->oplocks)
...@@ -319,9 +320,16 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid, ...@@ -319,9 +320,16 @@ cifs_do_create(struct inode *inode, struct dentry *direntry, unsigned int xid,
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
rc = server->ops->open(xid, tcon, full_path, disposition, oparms.tcon = tcon;
desired_access, create_options, fid, oplock, oparms.cifs_sb = cifs_sb;
buf, cifs_sb); oparms.desired_access = desired_access;
oparms.create_options = create_options;
oparms.disposition = disposition;
oparms.path = full_path;
oparms.fid = fid;
oparms.reconnect = false;
rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc) { if (rc) {
cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc); cifs_dbg(FYI, "cifs_create returned 0x%x\n", rc);
goto out; goto out;
......
...@@ -183,6 +183,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, ...@@ -183,6 +183,7 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
FILE_ALL_INFO *buf; FILE_ALL_INFO *buf;
struct TCP_Server_Info *server = tcon->ses->server; struct TCP_Server_Info *server = tcon->ses->server;
struct cifs_open_parms oparms;
if (!server->ops->open) if (!server->ops->open)
return -ENOSYS; return -ENOSYS;
...@@ -224,9 +225,16 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb, ...@@ -224,9 +225,16 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
if (backup_cred(cifs_sb)) if (backup_cred(cifs_sb))
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
rc = server->ops->open(xid, tcon, full_path, disposition, oparms.tcon = tcon;
desired_access, create_options, fid, oplock, buf, oparms.cifs_sb = cifs_sb;
cifs_sb); oparms.desired_access = desired_access;
oparms.create_options = create_options;
oparms.disposition = disposition;
oparms.path = full_path;
oparms.fid = fid;
oparms.reconnect = false;
rc = server->ops->open(xid, &oparms, oplock, buf);
if (rc) if (rc)
goto out; goto out;
...@@ -553,11 +561,10 @@ cifs_relock_file(struct cifsFileInfo *cfile) ...@@ -553,11 +561,10 @@ cifs_relock_file(struct cifsFileInfo *cfile)
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
int rc = 0; int rc = 0;
/* we are going to update can_cache_brlcks here - need a write access */ down_read(&cinode->lock_sem);
down_write(&cinode->lock_sem);
if (cinode->can_cache_brlcks) { if (cinode->can_cache_brlcks) {
/* can cache locks - no need to push them */ /* can cache locks - no need to relock */
up_write(&cinode->lock_sem); up_read(&cinode->lock_sem);
return rc; return rc;
} }
...@@ -568,7 +575,7 @@ cifs_relock_file(struct cifsFileInfo *cfile) ...@@ -568,7 +575,7 @@ cifs_relock_file(struct cifsFileInfo *cfile)
else else
rc = tcon->ses->server->ops->push_mand_locks(cfile); rc = tcon->ses->server->ops->push_mand_locks(cfile);
up_write(&cinode->lock_sem); up_read(&cinode->lock_sem);
return rc; return rc;
} }
...@@ -587,7 +594,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -587,7 +594,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
int desired_access; int desired_access;
int disposition = FILE_OPEN; int disposition = FILE_OPEN;
int create_options = CREATE_NOT_DIR; int create_options = CREATE_NOT_DIR;
struct cifs_fid fid; struct cifs_open_parms oparms;
xid = get_xid(); xid = get_xid();
mutex_lock(&cfile->fh_mutex); mutex_lock(&cfile->fh_mutex);
...@@ -637,7 +644,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -637,7 +644,7 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
rc = cifs_posix_open(full_path, NULL, inode->i_sb, rc = cifs_posix_open(full_path, NULL, inode->i_sb,
cifs_sb->mnt_file_mode /* ignored */, cifs_sb->mnt_file_mode /* ignored */,
oflags, &oplock, &fid.netfid, xid); oflags, &oplock, &cfile->fid.netfid, xid);
if (rc == 0) { if (rc == 0) {
cifs_dbg(FYI, "posix reopen succeeded\n"); cifs_dbg(FYI, "posix reopen succeeded\n");
goto reopen_success; goto reopen_success;
...@@ -654,7 +661,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -654,7 +661,16 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
create_options |= CREATE_OPEN_BACKUP_INTENT; create_options |= CREATE_OPEN_BACKUP_INTENT;
if (server->ops->get_lease_key) if (server->ops->get_lease_key)
server->ops->get_lease_key(inode, &fid); server->ops->get_lease_key(inode, &cfile->fid);
oparms.tcon = tcon;
oparms.cifs_sb = cifs_sb;
oparms.desired_access = desired_access;
oparms.create_options = create_options;
oparms.disposition = disposition;
oparms.path = full_path;
oparms.fid = &cfile->fid;
oparms.reconnect = true;
/* /*
* Can not refresh inode by passing in file_info buf to be returned by * Can not refresh inode by passing in file_info buf to be returned by
...@@ -663,9 +679,14 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -663,9 +679,14 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
* version of file size can be stale. If we knew for sure that inode was * version of file size can be stale. If we knew for sure that inode was
* not dirty locally we could do this. * not dirty locally we could do this.
*/ */
rc = server->ops->open(xid, tcon, full_path, disposition, rc = server->ops->open(xid, &oparms, &oplock, NULL);
desired_access, create_options, &fid, &oplock, if (rc == -ENOENT && oparms.reconnect == false) {
NULL, cifs_sb); /* durable handle timeout is expired - open the file again */
rc = server->ops->open(xid, &oparms, &oplock, NULL);
/* indicate that we need to relock the file */
oparms.reconnect = true;
}
if (rc) { if (rc) {
mutex_unlock(&cfile->fh_mutex); mutex_unlock(&cfile->fh_mutex);
cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc); cifs_dbg(FYI, "cifs_reopen returned 0x%x\n", rc);
...@@ -696,7 +717,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush) ...@@ -696,7 +717,8 @@ cifs_reopen_file(struct cifsFileInfo *cfile, bool can_flush)
* to the server to get the new inode info. * to the server to get the new inode info.
*/ */
server->ops->set_fid(cfile, &fid, oplock); server->ops->set_fid(cfile, &cfile->fid, oplock);
if (oparms.reconnect)
cifs_relock_file(cfile); cifs_relock_file(cfile);
reopen_error_exit: reopen_error_exit:
......
...@@ -558,6 +558,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info, ...@@ -558,6 +558,11 @@ cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
fattr->cf_mode &= ~(S_IWUGO); fattr->cf_mode &= ~(S_IWUGO);
fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks); fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
if (fattr->cf_nlink < 1) {
cifs_dbg(1, "replacing bogus file nlink value %u\n",
fattr->cf_nlink);
fattr->cf_nlink = 1;
}
} }
fattr->cf_uid = cifs_sb->mnt_uid; fattr->cf_uid = cifs_sb->mnt_uid;
......
...@@ -674,20 +674,23 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path, ...@@ -674,20 +674,23 @@ cifs_mkdir_setinfo(struct inode *inode, const char *full_path,
} }
static int static int
cifs_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, cifs_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
int disposition, int desired_access, int create_options, __u32 *oplock, FILE_ALL_INFO *buf)
struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf, {
struct cifs_sb_info *cifs_sb) if (!(oparms->tcon->ses->capabilities & CAP_NT_SMBS))
{ return SMBLegacyOpen(xid, oparms->tcon, oparms->path,
if (!(tcon->ses->capabilities & CAP_NT_SMBS)) oparms->disposition,
return SMBLegacyOpen(xid, tcon, path, disposition, oparms->desired_access,
desired_access, create_options, oparms->create_options,
&fid->netfid, oplock, buf, &oparms->fid->netfid, oplock, buf,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags oparms->cifs_sb->local_nls,
oparms->cifs_sb->mnt_cifs_flags
& CIFS_MOUNT_MAP_SPECIAL_CHR); & CIFS_MOUNT_MAP_SPECIAL_CHR);
return CIFSSMBOpen(xid, tcon, path, disposition, desired_access, return CIFSSMBOpen(xid, oparms->tcon, oparms->path,
create_options, &fid->netfid, oplock, buf, oparms->disposition, oparms->desired_access,
cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & oparms->create_options, &oparms->fid->netfid, oplock,
buf, oparms->cifs_sb->local_nls,
oparms->cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR); CIFS_MOUNT_MAP_SPECIAL_CHR);
} }
......
...@@ -40,7 +40,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) ...@@ -40,7 +40,8 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
oplock &= 0xFF; oplock &= 0xFF;
if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE) if (oplock == SMB2_OPLOCK_LEVEL_NOCHANGE)
return; return;
if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE) { if (oplock == SMB2_OPLOCK_LEVEL_EXCLUSIVE ||
oplock == SMB2_OPLOCK_LEVEL_BATCH) {
cinode->clientCanCacheAll = true; cinode->clientCanCacheAll = true;
cinode->clientCanCacheRead = true; cinode->clientCanCacheRead = true;
cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n", cifs_dbg(FYI, "Exclusive Oplock granted on inode %p\n",
...@@ -57,17 +58,16 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock) ...@@ -57,17 +58,16 @@ smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock)
} }
int int
smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, smb2_open_file(const unsigned int xid, struct cifs_open_parms *oparms,
int disposition, int desired_access, int create_options, __u32 *oplock, FILE_ALL_INFO *buf)
struct cifs_fid *fid, __u32 *oplock, FILE_ALL_INFO *buf,
struct cifs_sb_info *cifs_sb)
{ {
int rc; int rc;
__le16 *smb2_path; __le16 *smb2_path;
struct smb2_file_all_info *smb2_data = NULL; struct smb2_file_all_info *smb2_data = NULL;
__u8 smb2_oplock[17]; __u8 smb2_oplock[17];
struct cifs_fid *fid = oparms->fid;
smb2_path = cifs_convert_path_to_utf16(path, cifs_sb); smb2_path = cifs_convert_path_to_utf16(oparms->path, oparms->cifs_sb);
if (smb2_path == NULL) { if (smb2_path == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto out; goto out;
...@@ -80,21 +80,19 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path, ...@@ -80,21 +80,19 @@ smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, const char *path,
goto out; goto out;
} }
desired_access |= FILE_READ_ATTRIBUTES; oparms->desired_access |= FILE_READ_ATTRIBUTES;
*smb2_oplock = SMB2_OPLOCK_LEVEL_EXCLUSIVE; *smb2_oplock = SMB2_OPLOCK_LEVEL_BATCH;
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) if (oparms->tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING)
memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE); memcpy(smb2_oplock + 1, fid->lease_key, SMB2_LEASE_KEY_SIZE);
rc = SMB2_open(xid, tcon, smb2_path, &fid->persistent_fid, rc = SMB2_open(xid, oparms, smb2_path, smb2_oplock, smb2_data);
&fid->volatile_fid, desired_access, disposition,
0, 0, smb2_oplock, smb2_data);
if (rc) if (rc)
goto out; goto out;
if (buf) { if (buf) {
/* open response does not have IndexNumber field - get it */ /* open response does not have IndexNumber field - get it */
rc = SMB2_get_srv_num(xid, tcon, fid->persistent_fid, rc = SMB2_get_srv_num(xid, oparms->tcon, fid->persistent_fid,
fid->volatile_fid, fid->volatile_fid,
&smb2_data->IndexNumber); &smb2_data->IndexNumber);
if (rc) { if (rc) {
......
...@@ -41,21 +41,26 @@ static int ...@@ -41,21 +41,26 @@ static int
smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, smb2_open_op_close(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,
__u32 desired_access, __u32 create_disposition, __u32 desired_access, __u32 create_disposition,
__u32 file_attributes, __u32 create_options, __u32 create_options, void *data, int command)
void *data, int command)
{ {
int rc, tmprc = 0; int rc, tmprc = 0;
u64 persistent_fid, volatile_fid;
__le16 *utf16_path; __le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms;
struct cifs_fid fid;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path) if (!utf16_path)
return -ENOMEM; return -ENOMEM;
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, oparms.tcon = tcon;
desired_access, create_disposition, file_attributes, oparms.desired_access = desired_access;
create_options, &oplock, NULL); oparms.disposition = create_disposition;
oparms.create_options = create_options;
oparms.fid = &fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
...@@ -65,8 +70,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -65,8 +70,8 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
case SMB2_OP_DELETE: case SMB2_OP_DELETE:
break; break;
case SMB2_OP_QUERY_INFO: case SMB2_OP_QUERY_INFO:
tmprc = SMB2_query_info(xid, tcon, persistent_fid, tmprc = SMB2_query_info(xid, tcon, fid.persistent_fid,
volatile_fid, fid.volatile_fid,
(struct smb2_file_all_info *)data); (struct smb2_file_all_info *)data);
break; break;
case SMB2_OP_MKDIR: case SMB2_OP_MKDIR:
...@@ -76,19 +81,21 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -76,19 +81,21 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
*/ */
break; break;
case SMB2_OP_RENAME: case SMB2_OP_RENAME:
tmprc = SMB2_rename(xid, tcon, persistent_fid, volatile_fid, tmprc = SMB2_rename(xid, tcon, fid.persistent_fid,
(__le16 *)data); fid.volatile_fid, (__le16 *)data);
break; break;
case SMB2_OP_HARDLINK: case SMB2_OP_HARDLINK:
tmprc = SMB2_set_hardlink(xid, tcon, persistent_fid, tmprc = SMB2_set_hardlink(xid, tcon, fid.persistent_fid,
volatile_fid, (__le16 *)data); fid.volatile_fid, (__le16 *)data);
break; break;
case SMB2_OP_SET_EOF: case SMB2_OP_SET_EOF:
tmprc = SMB2_set_eof(xid, tcon, persistent_fid, volatile_fid, tmprc = SMB2_set_eof(xid, tcon, fid.persistent_fid,
current->tgid, (__le64 *)data); fid.volatile_fid, current->tgid,
(__le64 *)data);
break; break;
case SMB2_OP_SET_INFO: case SMB2_OP_SET_INFO:
tmprc = SMB2_set_info(xid, tcon, persistent_fid, volatile_fid, tmprc = SMB2_set_info(xid, tcon, fid.persistent_fid,
fid.volatile_fid,
(FILE_BASIC_INFO *)data); (FILE_BASIC_INFO *)data);
break; break;
default: default:
...@@ -96,7 +103,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -96,7 +103,7 @@ smb2_open_op_close(const unsigned int xid, struct cifs_tcon *tcon,
break; break;
} }
rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
if (tmprc) if (tmprc)
rc = tmprc; rc = tmprc;
kfree(utf16_path); kfree(utf16_path);
...@@ -129,8 +136,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -129,8 +136,8 @@ 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, 0, 0, FILE_READ_ATTRIBUTES, FILE_OPEN, 0, smb2_data,
smb2_data, SMB2_OP_QUERY_INFO); SMB2_OP_QUERY_INFO);
if (rc) if (rc)
goto out; goto out;
...@@ -145,7 +152,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -145,7 +152,7 @@ smb2_mkdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_open_op_close(xid, tcon, cifs_sb, name, return smb2_open_op_close(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR); CREATE_NOT_FILE, NULL, SMB2_OP_MKDIR);
} }
...@@ -164,7 +171,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name, ...@@ -164,7 +171,7 @@ smb2_mkdir_setinfo(struct inode *inode, const char *name,
dosattrs = cifs_i->cifsAttrs | ATTR_READONLY; dosattrs = cifs_i->cifsAttrs | ATTR_READONLY;
data.Attributes = cpu_to_le32(dosattrs); data.Attributes = cpu_to_le32(dosattrs);
tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name, tmprc = smb2_open_op_close(xid, tcon, cifs_sb, name,
FILE_WRITE_ATTRIBUTES, FILE_CREATE, 0, FILE_WRITE_ATTRIBUTES, FILE_CREATE,
CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO); CREATE_NOT_FILE, &data, SMB2_OP_SET_INFO);
if (tmprc == 0) if (tmprc == 0)
cifs_i->cifsAttrs = dosattrs; cifs_i->cifsAttrs = dosattrs;
...@@ -175,7 +182,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -175,7 +182,7 @@ smb2_rmdir(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
0, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE, CREATE_NOT_FILE | CREATE_DELETE_ON_CLOSE,
NULL, SMB2_OP_DELETE); NULL, SMB2_OP_DELETE);
} }
...@@ -184,7 +191,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name, ...@@ -184,7 +191,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
struct cifs_sb_info *cifs_sb) struct cifs_sb_info *cifs_sb)
{ {
return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN, return smb2_open_op_close(xid, tcon, cifs_sb, name, DELETE, FILE_OPEN,
0, CREATE_DELETE_ON_CLOSE, NULL, CREATE_DELETE_ON_CLOSE, NULL,
SMB2_OP_DELETE); SMB2_OP_DELETE);
} }
...@@ -203,7 +210,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -203,7 +210,7 @@ smb2_set_path_attr(const unsigned int xid, struct cifs_tcon *tcon,
} }
rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access, rc = smb2_open_op_close(xid, tcon, cifs_sb, from_name, access,
FILE_OPEN, 0, 0, smb2_to_name, command); FILE_OPEN, 0, smb2_to_name, command);
smb2_rename_path: smb2_rename_path:
kfree(smb2_to_name); kfree(smb2_to_name);
return rc; return rc;
...@@ -234,7 +241,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -234,7 +241,7 @@ smb2_set_path_size(const unsigned int xid, struct cifs_tcon *tcon,
{ {
__le64 eof = cpu_to_le64(size); __le64 eof = cpu_to_le64(size);
return smb2_open_op_close(xid, tcon, cifs_sb, full_path, return smb2_open_op_close(xid, tcon, cifs_sb, full_path,
FILE_WRITE_DATA, FILE_OPEN, 0, 0, &eof, FILE_WRITE_DATA, FILE_OPEN, 0, &eof,
SMB2_OP_SET_EOF); SMB2_OP_SET_EOF);
} }
...@@ -250,7 +257,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path, ...@@ -250,7 +257,7 @@ smb2_set_file_info(struct inode *inode, const char *full_path,
if (IS_ERR(tlink)) if (IS_ERR(tlink))
return PTR_ERR(tlink); return PTR_ERR(tlink);
rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path, rc = smb2_open_op_close(xid, tlink_tcon(tlink), cifs_sb, full_path,
FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, 0, buf, FILE_WRITE_ATTRIBUTES, FILE_OPEN, 0, buf,
SMB2_OP_SET_INFO); SMB2_OP_SET_INFO);
cifs_put_tlink(tlink); cifs_put_tlink(tlink);
return rc; return rc;
......
...@@ -213,22 +213,29 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -213,22 +213,29 @@ smb2_is_path_accessible(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)
{ {
int rc; int rc;
__u64 persistent_fid, volatile_fid;
__le16 *utf16_path; __le16 *utf16_path;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
struct cifs_open_parms oparms;
struct cifs_fid fid;
utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path) if (!utf16_path)
return -ENOMEM; return -ENOMEM;
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, oparms.tcon = tcon;
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL); 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, utf16_path, &oplock, NULL);
if (rc) { if (rc) {
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
} }
rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid); rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
kfree(utf16_path); kfree(utf16_path);
return rc; return rc;
} }
...@@ -443,15 +450,20 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -443,15 +450,20 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
__le16 *utf16_path; __le16 *utf16_path;
int rc; int rc;
__u8 oplock = SMB2_OPLOCK_LEVEL_NONE; __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
__u64 persistent_fid, volatile_fid; struct cifs_open_parms oparms;
utf16_path = cifs_convert_path_to_utf16(path, cifs_sb); utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
if (!utf16_path) if (!utf16_path)
return -ENOMEM; return -ENOMEM;
rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid, oparms.tcon = tcon;
FILE_READ_ATTRIBUTES | FILE_READ_DATA, FILE_OPEN, 0, 0, oparms.desired_access = FILE_READ_ATTRIBUTES | FILE_READ_DATA;
&oplock, NULL); oparms.disposition = FILE_OPEN;
oparms.create_options = 0;
oparms.fid = fid;
oparms.reconnect = false;
rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL);
kfree(utf16_path); kfree(utf16_path);
if (rc) { if (rc) {
cifs_dbg(VFS, "open dir failed\n"); cifs_dbg(VFS, "open dir failed\n");
...@@ -460,14 +472,12 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -460,14 +472,12 @@ smb2_query_dir_first(const unsigned int xid, struct cifs_tcon *tcon,
srch_inf->entries_in_buffer = 0; srch_inf->entries_in_buffer = 0;
srch_inf->index_of_last_entry = 0; srch_inf->index_of_last_entry = 0;
fid->persistent_fid = persistent_fid;
fid->volatile_fid = volatile_fid;
rc = SMB2_query_directory(xid, tcon, persistent_fid, volatile_fid, 0, rc = SMB2_query_directory(xid, tcon, fid->persistent_fid,
srch_inf); fid->volatile_fid, 0, srch_inf);
if (rc) { if (rc) {
cifs_dbg(VFS, "query directory failed\n"); cifs_dbg(VFS, "query directory failed\n");
SMB2_close(xid, tcon, persistent_fid, volatile_fid); SMB2_close(xid, tcon, fid->persistent_fid, fid->volatile_fid);
} }
return rc; return rc;
} }
...@@ -528,17 +538,25 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -528,17 +538,25 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
struct kstatfs *buf) struct kstatfs *buf)
{ {
int rc; int rc;
u64 persistent_fid, volatile_fid;
__le16 srch_path = 0; /* Null - open root of share */ __le16 srch_path = 0; /* Null - open root of share */
u8 oplock = SMB2_OPLOCK_LEVEL_NONE; 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, tcon, &srch_path, &persistent_fid, &volatile_fid, rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL);
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0, &oplock, NULL);
if (rc) if (rc)
return rc; return rc;
buf->f_type = SMB2_MAGIC_NUMBER; buf->f_type = SMB2_MAGIC_NUMBER;
rc = SMB2_QFS_info(xid, tcon, persistent_fid, volatile_fid, buf); rc = SMB2_QFS_info(xid, tcon, fid.persistent_fid, fid.volatile_fid,
SMB2_close(xid, tcon, persistent_fid, volatile_fid); buf);
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
return rc; return rc;
} }
......
...@@ -847,29 +847,76 @@ create_lease_buf(u8 *lease_key, u8 oplock) ...@@ -847,29 +847,76 @@ create_lease_buf(u8 *lease_key, u8 oplock)
return buf; return buf;
} }
static struct create_durable *
create_durable_buf(void)
{
struct create_durable *buf;
buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_durable, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Name[0] = 'D';
buf->Name[1] = 'H';
buf->Name[2] = 'n';
buf->Name[3] = 'Q';
return buf;
}
static struct create_durable *
create_reconnect_durable_buf(struct cifs_fid *fid)
{
struct create_durable *buf;
buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
if (!buf)
return NULL;
buf->ccontext.DataOffset = cpu_to_le16(offsetof
(struct create_durable, Data));
buf->ccontext.DataLength = cpu_to_le32(16);
buf->ccontext.NameOffset = cpu_to_le16(offsetof
(struct create_durable, Name));
buf->ccontext.NameLength = cpu_to_le16(4);
buf->Data.Fid.PersistentFileId = fid->persistent_fid;
buf->Data.Fid.VolatileFileId = fid->volatile_fid;
buf->Name[0] = 'D';
buf->Name[1] = 'H';
buf->Name[2] = 'n';
buf->Name[3] = 'C';
return buf;
}
static __u8 static __u8
parse_lease_state(struct smb2_create_rsp *rsp) parse_lease_state(struct smb2_create_rsp *rsp)
{ {
char *data_offset; char *data_offset;
struct create_lease *lc; struct create_lease *lc;
bool found = false; bool found = false;
unsigned int next = 0;
char *name;
data_offset = (char *)rsp; data_offset = (char *)rsp + 4 + le32_to_cpu(rsp->CreateContextsOffset);
data_offset += 4 + le32_to_cpu(rsp->CreateContextsOffset);
lc = (struct create_lease *)data_offset; lc = (struct create_lease *)data_offset;
do { do {
char *name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc; lc = (struct create_lease *)((char *)lc + next);
name = le16_to_cpu(lc->ccontext.NameOffset) + (char *)lc;
if (le16_to_cpu(lc->ccontext.NameLength) != 4 || if (le16_to_cpu(lc->ccontext.NameLength) != 4 ||
strncmp(name, "RqLs", 4)) { strncmp(name, "RqLs", 4)) {
lc = (struct create_lease *)((char *)lc next = le32_to_cpu(lc->ccontext.Next);
+ le32_to_cpu(lc->ccontext.Next));
continue; continue;
} }
if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS) if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS)
return SMB2_OPLOCK_LEVEL_NOCHANGE; return SMB2_OPLOCK_LEVEL_NOCHANGE;
found = true; found = true;
break; break;
} while (le32_to_cpu(lc->ccontext.Next) != 0); } while (next != 0);
if (!found) if (!found)
return 0; return 0;
...@@ -877,23 +924,74 @@ parse_lease_state(struct smb2_create_rsp *rsp) ...@@ -877,23 +924,74 @@ parse_lease_state(struct smb2_create_rsp *rsp)
return smb2_map_lease_to_oplock(lc->lcontext.LeaseState); return smb2_map_lease_to_oplock(lc->lcontext.LeaseState);
} }
static int
add_lease_context(struct kvec *iov, unsigned int *num_iovec, __u8 *oplock)
{
struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec;
iov[num].iov_base = create_lease_buf(oplock+1, *oplock);
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = sizeof(struct create_lease);
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
if (!req->CreateContextsOffset)
req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) - 4 +
iov[num - 1].iov_len);
req->CreateContextsLength = cpu_to_le32(
le32_to_cpu(req->CreateContextsLength) +
sizeof(struct create_lease));
inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
*num_iovec = num + 1;
return 0;
}
static int
add_durable_context(struct kvec *iov, unsigned int *num_iovec,
struct cifs_open_parms *oparms)
{
struct smb2_create_req *req = iov[0].iov_base;
unsigned int num = *num_iovec;
if (oparms->reconnect) {
iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
/* indicate that we don't need to relock the file */
oparms->reconnect = false;
} else
iov[num].iov_base = create_durable_buf();
if (iov[num].iov_base == NULL)
return -ENOMEM;
iov[num].iov_len = sizeof(struct create_durable);
if (!req->CreateContextsOffset)
req->CreateContextsOffset =
cpu_to_le32(sizeof(struct smb2_create_req) - 4 +
iov[1].iov_len);
req->CreateContextsLength =
cpu_to_le32(le32_to_cpu(req->CreateContextsLength) +
sizeof(struct create_durable));
inc_rfc1001_len(&req->hdr, sizeof(struct create_durable));
*num_iovec = num + 1;
return 0;
}
int int
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
__u32 create_disposition, __u32 file_attributes, __u32 create_options,
__u8 *oplock, struct smb2_file_all_info *buf) __u8 *oplock, struct smb2_file_all_info *buf)
{ {
struct smb2_create_req *req; struct smb2_create_req *req;
struct smb2_create_rsp *rsp; struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server; struct TCP_Server_Info *server;
struct cifs_tcon *tcon = oparms->tcon;
struct cifs_ses *ses = tcon->ses; struct cifs_ses *ses = tcon->ses;
struct kvec iov[3]; struct kvec iov[4];
int resp_buftype; int resp_buftype;
int uni_path_len; int uni_path_len;
__le16 *copy_path = NULL; __le16 *copy_path = NULL;
int copy_size; int copy_size;
int rc = 0; int rc = 0;
int num_iovecs = 2; unsigned int num_iovecs = 2;
__u32 file_attributes = 0;
cifs_dbg(FYI, "create/open\n"); cifs_dbg(FYI, "create/open\n");
...@@ -906,23 +1004,25 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, ...@@ -906,23 +1004,25 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
if (rc) if (rc)
return rc; return rc;
if (oparms->create_options & CREATE_OPTION_READONLY)
file_attributes |= ATTR_READONLY;
req->ImpersonationLevel = IL_IMPERSONATION; req->ImpersonationLevel = IL_IMPERSONATION;
req->DesiredAccess = cpu_to_le32(desired_access); req->DesiredAccess = cpu_to_le32(oparms->desired_access);
/* File attributes ignored on open (used in create though) */ /* File attributes ignored on open (used in create though) */
req->FileAttributes = cpu_to_le32(file_attributes); req->FileAttributes = cpu_to_le32(file_attributes);
req->ShareAccess = FILE_SHARE_ALL_LE; req->ShareAccess = FILE_SHARE_ALL_LE;
req->CreateDisposition = cpu_to_le32(create_disposition); req->CreateDisposition = cpu_to_le32(oparms->disposition);
req->CreateOptions = cpu_to_le32(create_options); req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2; uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) /* do not count rfc1001 len field */
- 8 /* pad */ - 4 /* do not count rfc1001 len field */); req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req) - 4);
iov[0].iov_base = (char *)req; iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */ /* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4; iov[0].iov_len = get_rfc1002_length(req) + 4;
/* MUST set path len (NameLength) to 0 opening root of share */ /* MUST set path len (NameLength) to 0 opening root of share */
if (uni_path_len >= 4) {
req->NameLength = cpu_to_le16(uni_path_len - 2); req->NameLength = cpu_to_le16(uni_path_len - 2);
/* -1 since last byte is buf[0] which is sent below (path) */ /* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len--; iov[0].iov_len--;
...@@ -942,18 +1042,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, ...@@ -942,18 +1042,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
iov[1].iov_len = uni_path_len; iov[1].iov_len = uni_path_len;
iov[1].iov_base = path; iov[1].iov_base = path;
/* /* -1 since last byte is buf[0] which was counted in smb2_buf_len */
* -1 since last byte is buf[0] which was counted in
* smb2_buf_len.
*/
inc_rfc1001_len(req, uni_path_len - 1); inc_rfc1001_len(req, uni_path_len - 1);
} else {
iov[0].iov_len += 7;
req->hdr.smb2_buf_length = cpu_to_be32(be32_to_cpu(
req->hdr.smb2_buf_length) + 8 - 1);
num_iovecs = 1;
req->NameLength = 0;
}
if (!server->oplocks) if (!server->oplocks)
*oplock = SMB2_OPLOCK_LEVEL_NONE; *oplock = SMB2_OPLOCK_LEVEL_NONE;
...@@ -962,21 +1052,29 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, ...@@ -962,21 +1052,29 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
*oplock == SMB2_OPLOCK_LEVEL_NONE) *oplock == SMB2_OPLOCK_LEVEL_NONE)
req->RequestedOplockLevel = *oplock; req->RequestedOplockLevel = *oplock;
else { else {
iov[num_iovecs].iov_base = create_lease_buf(oplock+1, *oplock); rc = add_lease_context(iov, &num_iovecs, oplock);
if (iov[num_iovecs].iov_base == NULL) { if (rc) {
cifs_small_buf_release(req); cifs_small_buf_release(req);
kfree(copy_path); kfree(copy_path);
return -ENOMEM; return rc;
}
}
if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
/* need to set Next field of lease context if we request it */
if (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_LEASING) {
struct create_context *ccontext =
(struct create_context *)iov[num_iovecs-1].iov_base;
ccontext->Next =
cpu_to_le32(sizeof(struct create_lease));
}
rc = add_durable_context(iov, &num_iovecs, oparms);
if (rc) {
cifs_small_buf_release(req);
kfree(copy_path);
kfree(iov[num_iovecs-1].iov_base);
return rc;
} }
iov[num_iovecs].iov_len = sizeof(struct create_lease);
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
req->CreateContextsOffset = cpu_to_le32(
sizeof(struct smb2_create_req) - 4 - 8 +
iov[num_iovecs-1].iov_len);
req->CreateContextsLength = cpu_to_le32(
sizeof(struct create_lease));
inc_rfc1001_len(&req->hdr, sizeof(struct create_lease));
num_iovecs++;
} }
rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0); rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
...@@ -987,8 +1085,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path, ...@@ -987,8 +1085,8 @@ SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
goto creat_exit; goto creat_exit;
} }
*persistent_fid = rsp->PersistentFileId; oparms->fid->persistent_fid = rsp->PersistentFileId;
*volatile_fid = rsp->VolatileFileId; oparms->fid->volatile_fid = rsp->VolatileFileId;
if (buf) { if (buf) {
memcpy(buf, &rsp->CreationTime, 32); memcpy(buf, &rsp->CreationTime, 32);
......
...@@ -428,7 +428,7 @@ struct smb2_create_req { ...@@ -428,7 +428,7 @@ struct smb2_create_req {
__le16 NameLength; __le16 NameLength;
__le32 CreateContextsOffset; __le32 CreateContextsOffset;
__le32 CreateContextsLength; __le32 CreateContextsLength;
__u8 Buffer[8]; __u8 Buffer[0];
} __packed; } __packed;
struct smb2_create_rsp { struct smb2_create_rsp {
...@@ -485,6 +485,18 @@ struct create_lease { ...@@ -485,6 +485,18 @@ struct create_lease {
struct lease_context lcontext; struct lease_context lcontext;
} __packed; } __packed;
struct create_durable {
struct create_context ccontext;
__u8 Name[8];
union {
__u8 Reserved[16];
struct {
__u64 PersistentFileId;
__u64 VolatileFileId;
} Fid;
} Data;
} __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[24];
......
...@@ -84,11 +84,9 @@ extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -84,11 +84,9 @@ extern int smb2_create_hardlink(const unsigned int xid, struct cifs_tcon *tcon,
const char *from_name, const char *to_name, const char *from_name, const char *to_name,
struct cifs_sb_info *cifs_sb); struct cifs_sb_info *cifs_sb);
extern int smb2_open_file(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_open_file(const unsigned int xid,
const char *full_path, int disposition, struct cifs_open_parms *oparms,
int desired_access, int create_options, __u32 *oplock, FILE_ALL_INFO *buf);
struct cifs_fid *fid, __u32 *oplock,
FILE_ALL_INFO *buf, struct cifs_sb_info *cifs_sb);
extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock); extern void smb2_set_oplock_level(struct cifsInodeInfo *cinode, __u32 oplock);
extern int smb2_unlock_range(struct cifsFileInfo *cfile, extern int smb2_unlock_range(struct cifsFileInfo *cfile,
struct file_lock *flock, const unsigned int xid); struct file_lock *flock, const unsigned int xid);
...@@ -106,11 +104,9 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, ...@@ -106,11 +104,9 @@ extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
const char *tree, struct cifs_tcon *tcon, const char *tree, struct cifs_tcon *tcon,
const struct nls_table *); const struct nls_table *);
extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon); extern int SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon);
extern int SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms,
__le16 *path, u64 *persistent_fid, u64 *volatile_fid, __le16 *path, __u8 *oplock,
__u32 desired_access, __u32 create_disposition, struct smb2_file_all_info *buf);
__u32 file_attributes, __u32 create_options,
__u8 *oplock, struct smb2_file_all_info *buf);
extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, extern int SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid, u32 opcode, u64 persistent_fid, u64 volatile_fid, u32 opcode,
bool is_fsctl, char *in_data, u32 indatalen, bool is_fsctl, char *in_data, u32 indatalen,
......
...@@ -39,6 +39,77 @@ ...@@ -39,6 +39,77 @@
#include "smb2status.h" #include "smb2status.h"
#include "smb2glob.h" #include "smb2glob.h"
static int
smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
{
unsigned int size;
if (server->secmech.sdeschmacsha256 != NULL)
return 0; /* already allocated */
server->secmech.hmacsha256 = crypto_alloc_shash("hmac(sha256)", 0, 0);
if (IS_ERR(server->secmech.hmacsha256)) {
cifs_dbg(VFS, "could not allocate crypto hmacsha256\n");
return PTR_ERR(server->secmech.hmacsha256);
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.hmacsha256);
server->secmech.sdeschmacsha256 = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdeschmacsha256) {
crypto_free_shash(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
return -ENOMEM;
}
server->secmech.sdeschmacsha256->shash.tfm = server->secmech.hmacsha256;
server->secmech.sdeschmacsha256->shash.flags = 0x0;
return 0;
}
static int
smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
{
unsigned int size;
int rc;
if (server->secmech.sdesccmacaes != NULL)
return 0; /* already allocated */
rc = smb2_crypto_shash_allocate(server);
if (rc)
return rc;
server->secmech.cmacaes = crypto_alloc_shash("cmac(aes)", 0, 0);
if (IS_ERR(server->secmech.cmacaes)) {
cifs_dbg(VFS, "could not allocate crypto cmac-aes");
kfree(server->secmech.sdeschmacsha256);
server->secmech.sdeschmacsha256 = NULL;
crypto_free_shash(server->secmech.hmacsha256);
server->secmech.hmacsha256 = NULL;
return PTR_ERR(server->secmech.cmacaes);
}
size = sizeof(struct shash_desc) +
crypto_shash_descsize(server->secmech.cmacaes);
server->secmech.sdesccmacaes = kmalloc(size, GFP_KERNEL);
if (!server->secmech.sdesccmacaes) {
cifs_dbg(VFS, "%s: Can't alloc cmacaes\n", __func__);
kfree(server->secmech.sdeschmacsha256);
server->secmech.sdeschmacsha256 = NULL;
crypto_free_shash(server->secmech.hmacsha256);
crypto_free_shash(server->secmech.cmacaes);
server->secmech.hmacsha256 = NULL;
server->secmech.cmacaes = NULL;
return -ENOMEM;
}
server->secmech.sdesccmacaes->shash.tfm = server->secmech.cmacaes;
server->secmech.sdesccmacaes->shash.flags = 0x0;
return 0;
}
int int
smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
{ {
...@@ -52,6 +123,12 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -52,6 +123,12 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE); memset(smb2_signature, 0x0, SMB2_HMACSHA256_SIZE);
memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE); memset(smb2_pdu->Signature, 0x0, SMB2_SIGNATURE_SIZE);
rc = smb2_crypto_shash_allocate(server);
if (rc) {
cifs_dbg(VFS, "%s: shah256 alloc failed\n", __func__);
return rc;
}
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
...@@ -61,7 +138,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -61,7 +138,7 @@ smb2_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash); rc = crypto_shash_init(&server->secmech.sdeschmacsha256->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init md5\n", __func__); cifs_dbg(VFS, "%s: Could not init sha256", __func__);
return rc; return rc;
} }
...@@ -129,6 +206,12 @@ generate_smb3signingkey(struct TCP_Server_Info *server) ...@@ -129,6 +206,12 @@ generate_smb3signingkey(struct TCP_Server_Info *server)
memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE); memset(prfhash, 0x0, SMB2_HMACSHA256_SIZE);
memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE); memset(server->smb3signingkey, 0x0, SMB3_SIGNKEY_SIZE);
rc = smb3_crypto_shash_allocate(server);
if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
goto smb3signkey_ret;
}
rc = crypto_shash_setkey(server->secmech.hmacsha256, rc = crypto_shash_setkey(server->secmech.hmacsha256,
server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE); server->session_key.response, SMB2_NTLMV2_SESSKEY_SIZE);
if (rc) { if (rc) {
...@@ -210,6 +293,11 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -210,6 +293,11 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
return rc; return rc;
} }
/*
* we already allocate sdesccmacaes when we init smb3 signing key,
* so unlike smb2 case we do not have to check here if secmech are
* initialized
*/
rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash); rc = crypto_shash_init(&server->secmech.sdesccmacaes->shash);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__); cifs_dbg(VFS, "%s: Could not init cmac aes\n", __func__);
......
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