Skip to content

Commit

Permalink
CIFS: Add SMB2 support for is_path_accessible
Browse files Browse the repository at this point in the history
that needs for a successful mount through SMB2 protocol.

Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <smfrench@gmail.com>
  • Loading branch information
piastry committed Jul 24, 2012
1 parent 68889f2 commit 2503a0d
Show file tree
Hide file tree
Showing 7 changed files with 423 additions and 0 deletions.
61 changes: 61 additions & 0 deletions fs/cifs/cifs_unicode.c
Original file line number Diff line number Diff line change
Expand Up @@ -330,3 +330,64 @@ cifsConvertToUTF16(__le16 *target, const char *source, int srclen,
ctoUTF16_out:
return i;
}

#ifdef CONFIG_CIFS_SMB2
/*
* cifs_local_to_utf16_bytes - how long will a string be after conversion?
* @from - pointer to input string
* @maxbytes - don't go past this many bytes of input string
* @codepage - source codepage
*
* Walk a string and return the number of bytes that the string will
* be after being converted to the given charset, not including any null
* termination required. Don't walk past maxbytes in the source buffer.
*/

static int
cifs_local_to_utf16_bytes(const char *from, int len,
const struct nls_table *codepage)
{
int charlen;
int i;
wchar_t wchar_to;

for (i = 0; len && *from; i++, from += charlen, len -= charlen) {
charlen = codepage->char2uni(from, len, &wchar_to);
/* Failed conversion defaults to a question mark */
if (charlen < 1)
charlen = 1;
}
return 2 * i; /* UTF16 characters are two bytes */
}

/*
* cifs_strndup_to_utf16 - copy a string to wire format from the local codepage
* @src - source string
* @maxlen - don't walk past this many bytes in the source string
* @utf16_len - the length of the allocated string in bytes (including null)
* @cp - source codepage
* @remap - map special chars
*
* Take a string convert it from the local codepage to UTF16 and
* put it in a new buffer. Returns a pointer to the new string or NULL on
* error.
*/
__le16 *
cifs_strndup_to_utf16(const char *src, const int maxlen, int *utf16_len,
const struct nls_table *cp, int remap)
{
int len;
__le16 *dst;

len = cifs_local_to_utf16_bytes(src, maxlen, cp);
len += 2; /* NULL */
dst = kmalloc(len, GFP_KERNEL);
if (!dst) {
*utf16_len = 0;
return NULL;
}
cifsConvertToUTF16(dst, src, strlen(src), cp, remap);
*utf16_len = len;
return dst;
}
#endif /* CONFIG_CIFS_SMB2 */
5 changes: 5 additions & 0 deletions fs/cifs/cifs_unicode.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,11 @@ char *cifs_strndup_from_utf16(const char *src, const int maxlen,
const struct nls_table *codepage);
extern int cifsConvertToUTF16(__le16 *target, const char *source, int maxlen,
const struct nls_table *cp, int mapChars);
#ifdef CONFIG_CIFS_SMB2
extern __le16 *cifs_strndup_to_utf16(const char *src, const int maxlen,
int *utf16_len, const struct nls_table *cp,
int remap);
#endif /* CONFIG_CIFS_SMB2 */
#endif

/*
Expand Down
25 changes: 25 additions & 0 deletions fs/cifs/smb2misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ smb2_get_data_area_len(int *off, int *len, struct smb2_hdr *hdr)
((struct smb2_sess_setup_rsp *)hdr)->SecurityBufferLength);
break;
case SMB2_CREATE:
*off = le32_to_cpu(
((struct smb2_create_rsp *)hdr)->CreateContextsOffset);
*len = le32_to_cpu(
((struct smb2_create_rsp *)hdr)->CreateContextsLength);
break;
case SMB2_READ:
case SMB2_QUERY_INFO:
case SMB2_QUERY_DIRECTORY:
Expand Down Expand Up @@ -315,3 +320,23 @@ smb2_calc_size(struct smb2_hdr *hdr)
cFYI(1, "SMB2 len %d", len);
return len;
}

/* Note: caller must free return buffer */
__le16 *
cifs_convert_path_to_utf16(const char *from, struct cifs_sb_info *cifs_sb)
{
int len;
const char *start_of_path;
__le16 *to;

/* Windows doesn't allow paths beginning with \ */
if (from[0] == '\\')
start_of_path = from + 1;
else
start_of_path = from;
to = cifs_strndup_to_utf16(start_of_path, PATH_MAX, &len,
cifs_sb->local_nls,
cifs_sb->mnt_cifs_flags &
CIFS_MOUNT_MAP_SPECIAL_CHR);
return to;
}
25 changes: 25 additions & 0 deletions fs/cifs/smb2ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,30 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
return rc;
}

static int
smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
struct cifs_sb_info *cifs_sb, const char *full_path)
{
int rc;
__u64 persistent_fid, volatile_fid;
__le16 *utf16_path;

utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
if (!utf16_path)
return -ENOMEM;

rc = SMB2_open(xid, tcon, utf16_path, &persistent_fid, &volatile_fid,
FILE_READ_ATTRIBUTES, FILE_OPEN, 0, 0);
if (rc) {
kfree(utf16_path);
return rc;
}

rc = SMB2_close(xid, tcon, persistent_fid, volatile_fid);
kfree(utf16_path);
return rc;
}

struct smb_version_operations smb21_operations = {
.setup_request = smb2_setup_request,
.check_receive = smb2_check_receive,
Expand All @@ -174,6 +198,7 @@ struct smb_version_operations smb21_operations = {
.logoff = SMB2_logoff,
.tree_connect = SMB2_tcon,
.tree_disconnect = SMB2_tdis,
.is_path_accessible = smb2_is_path_accessible,
};

struct smb_version_values smb21_values = {
Expand Down
132 changes: 132 additions & 0 deletions fs/cifs/smb2pdu.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,3 +829,135 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)

return rc;
}

int
SMB2_open(const unsigned int xid, struct cifs_tcon *tcon, __le16 *path,
u64 *persistent_fid, u64 *volatile_fid, __u32 desired_access,
__u32 create_disposition, __u32 file_attributes, __u32 create_options)
{
struct smb2_create_req *req;
struct smb2_create_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[2];
int resp_buftype;
int uni_path_len;
int rc = 0;
int num_iovecs = 2;

cFYI(1, "create/open");

if (ses && (ses->server))
server = ses->server;
else
return -EIO;

rc = small_smb2_init(SMB2_CREATE, tcon, (void **) &req);
if (rc)
return rc;

if (enable_oplocks)
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_BATCH;
else
req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
req->ImpersonationLevel = IL_IMPERSONATION;
req->DesiredAccess = cpu_to_le32(desired_access);
/* File attributes ignored on open (used in create though) */
req->FileAttributes = cpu_to_le32(file_attributes);
req->ShareAccess = FILE_SHARE_ALL_LE;
req->CreateDisposition = cpu_to_le32(create_disposition);
req->CreateOptions = cpu_to_le32(create_options);
uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req)
- 1 /* pad */ - 4 /* do not count rfc1001 len field */);

iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;

/* 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);
/* -1 since last byte is buf[0] which is sent below (path) */
iov[0].iov_len--;
iov[1].iov_len = uni_path_len;
iov[1].iov_base = path;
/*
* -1 since last byte is buf[0] which was counted in
* smb2_buf_len.
*/
inc_rfc1001_len(req, uni_path_len - 1);
} else {
num_iovecs = 1;
req->NameLength = 0;
}

rc = SendReceive2(xid, ses, iov, num_iovecs, &resp_buftype, 0);
rsp = (struct smb2_create_rsp *)iov[0].iov_base;

if (rc != 0) {
cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
goto creat_exit;
}

if (rsp == NULL) {
rc = -EIO;
goto creat_exit;
}
*persistent_fid = rsp->PersistentFileId;
*volatile_fid = rsp->VolatileFileId;
creat_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}

int
SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
u64 persistent_fid, u64 volatile_fid)
{
struct smb2_close_req *req;
struct smb2_close_rsp *rsp;
struct TCP_Server_Info *server;
struct cifs_ses *ses = tcon->ses;
struct kvec iov[1];
int resp_buftype;
int rc = 0;

cFYI(1, "Close");

if (ses && (ses->server))
server = ses->server;
else
return -EIO;

rc = small_smb2_init(SMB2_CLOSE, tcon, (void **) &req);
if (rc)
return rc;

req->PersistentFileId = persistent_fid;
req->VolatileFileId = volatile_fid;

iov[0].iov_base = (char *)req;
/* 4 for rfc1002 length field */
iov[0].iov_len = get_rfc1002_length(req) + 4;

rc = SendReceive2(xid, ses, iov, 1, &resp_buftype, 0);
rsp = (struct smb2_close_rsp *)iov[0].iov_base;

if (rc != 0) {
if (tcon)
cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE);
goto close_exit;
}

if (rsp == NULL) {
rc = -EIO;
goto close_exit;
}

/* BB FIXME - decode close response, update inode for caching */

close_exit:
free_rsp_buf(resp_buftype, rsp);
return rc;
}
Loading

0 comments on commit 2503a0d

Please sign in to comment.