Commit 3d519bd1 authored by Aurelien Aptel's avatar Aurelien Aptel Committed by Steve French

cifs: plumb smb2 POSIX dir enumeration

* add code to request POSIX info level
* parse dir entries and fill cifs_fattr to get correct inode data

since the POSIX payload is variable size the number of entries in a
FIND response needs to be computed differently.

Dirs and regular files are properly reported along with mode bits,
hardlink number, c/m/atime. No special files yet (see below).

Current experimental version of Samba with the extension unfortunately
has issues with wildcards and needs the following patch:

> --- i/source3/smbd/smb2_query_directory.c
> +++ w/source3/smbd/smb2_query_directory.c
> @@ -397,9 +397,7 @@ smbd_smb2_query_directory_send(TALLOC_CTX
> *mem_ctx,
> 		}
> 	}
>
> -       if (!state->smbreq->posix_pathnames) {
> 		wcard_has_wild = ms_has_wild(state->in_file_name);
> -       }
>
> 	/* Ensure we've canonicalized any search path if not a wildcard. */
> 	if (!wcard_has_wild) {
>

Also for special files despite reporting them as reparse point samba
doesn't set the reparse tag field. This patch will mark them as needing
re-evaluation but the re-evaluate code doesn't deal with it yet.
Signed-off-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 349e13ad
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "cifs_debug.h" #include "cifs_debug.h"
#include "cifs_fs_sb.h" #include "cifs_fs_sb.h"
#include "cifsfs.h" #include "cifsfs.h"
#include "smb2proto.h"
/* /*
* To be safe - for UCS to UTF-8 with strings loaded with the rare long * To be safe - for UCS to UTF-8 with strings loaded with the rare long
...@@ -217,6 +218,60 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb) ...@@ -217,6 +218,60 @@ cifs_fill_common_info(struct cifs_fattr *fattr, struct cifs_sb_info *cifs_sb)
} }
} }
/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */
static void
cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
struct cifs_sb_info *cifs_sb)
{
struct smb2_posix_info_parsed parsed;
posix_info_parse(info, NULL, &parsed);
memset(fattr, 0, sizeof(*fattr));
fattr->cf_uniqueid = le64_to_cpu(info->Inode);
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime);
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
/*
* Since we set the inode type below we need to mask off
* to avoid strange results if bits set above.
* XXX: why not make server&client use the type bits?
*/
fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
cifs_dbg(VFS, "XXX dev %d, reparse %d, mode %o",
le32_to_cpu(info->DeviceId),
le32_to_cpu(info->ReparseTag),
le32_to_cpu(info->Mode));
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
fattr->cf_mode |= S_IFDIR;
fattr->cf_dtype = DT_DIR;
} else {
/*
* mark anything that is not a dir as regular
* file. special files should have the REPARSE
* attribute and will be marked as needing revaluation
*/
fattr->cf_mode |= S_IFREG;
fattr->cf_dtype = DT_REG;
}
if (reparse_file_needs_reval(fattr))
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
/* TODO map SIDs */
fattr->cf_uid = cifs_sb->mnt_uid;
fattr->cf_gid = cifs_sb->mnt_gid;
}
static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info) static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
{ {
const FILE_DIRECTORY_INFO *fi = info; const FILE_DIRECTORY_INFO *fi = info;
...@@ -359,6 +414,8 @@ initiate_cifs_search(const unsigned int xid, struct file *file) ...@@ -359,6 +414,8 @@ initiate_cifs_search(const unsigned int xid, struct file *file)
/* if (cap_unix(tcon->ses) { */ /* if (cap_unix(tcon->ses) { */
if (tcon->unix_ext) if (tcon->unix_ext)
cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX; cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
else if (tcon->posix_extensions)
cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO;
else if ((tcon->ses->capabilities & else if ((tcon->ses->capabilities &
tcon->ses->server->vals->cap_nt_find) == 0) { tcon->ses->server->vals->cap_nt_find) == 0) {
cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD; cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
...@@ -451,6 +508,23 @@ struct cifs_dirent { ...@@ -451,6 +508,23 @@ struct cifs_dirent {
u64 ino; u64 ino;
}; };
static void cifs_fill_dirent_posix(struct cifs_dirent *de,
const struct smb2_posix_info *info)
{
struct smb2_posix_info_parsed parsed;
/* payload should have already been checked at this point */
if (posix_info_parse(info, NULL, &parsed) < 0) {
cifs_dbg(VFS, "invalid POSIX info payload");
return;
}
de->name = parsed.name;
de->namelen = parsed.name_len;
de->resume_key = info->Ignored;
de->ino = le64_to_cpu(info->Inode);
}
static void cifs_fill_dirent_unix(struct cifs_dirent *de, static void cifs_fill_dirent_unix(struct cifs_dirent *de,
const FILE_UNIX_INFO *info, bool is_unicode) const FILE_UNIX_INFO *info, bool is_unicode)
{ {
...@@ -511,6 +585,9 @@ static int cifs_fill_dirent(struct cifs_dirent *de, const void *info, ...@@ -511,6 +585,9 @@ static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
memset(de, 0, sizeof(*de)); memset(de, 0, sizeof(*de));
switch (level) { switch (level) {
case SMB_FIND_FILE_POSIX_INFO:
cifs_fill_dirent_posix(de, info);
break;
case SMB_FIND_FILE_UNIX: case SMB_FIND_FILE_UNIX:
cifs_fill_dirent_unix(de, info, is_unicode); cifs_fill_dirent_unix(de, info, is_unicode);
break; break;
...@@ -786,6 +863,11 @@ static int cifs_filldir(char *find_entry, struct file *file, ...@@ -786,6 +863,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
} }
switch (file_info->srch_inf.info_level) { switch (file_info->srch_inf.info_level) {
case SMB_FIND_FILE_POSIX_INFO:
cifs_posix_to_fattr(&fattr,
(struct smb2_posix_info *)find_entry,
cifs_sb);
break;
case SMB_FIND_FILE_UNIX: case SMB_FIND_FILE_UNIX:
cifs_unix_basic_to_fattr(&fattr, cifs_unix_basic_to_fattr(&fattr,
&((FILE_UNIX_INFO *)find_entry)->basic, &((FILE_UNIX_INFO *)find_entry)->basic,
......
...@@ -4398,7 +4398,8 @@ static int posix_info_extra_size(const void *beg, const void *end) ...@@ -4398,7 +4398,8 @@ static int posix_info_extra_size(const void *beg, const void *end)
} }
static unsigned int static unsigned int
num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry,
size_t size)
{ {
int len; int len;
unsigned int entrycount = 0; unsigned int entrycount = 0;
...@@ -4422,8 +4423,13 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size) ...@@ -4422,8 +4423,13 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
entryptr = entryptr + next_offset; entryptr = entryptr + next_offset;
dir_info = (FILE_DIRECTORY_INFO *)entryptr; dir_info = (FILE_DIRECTORY_INFO *)entryptr;
len = le32_to_cpu(dir_info->FileNameLength); if (infotype == SMB_FIND_FILE_POSIX_INFO)
if (entryptr + len < entryptr || len = posix_info_extra_size(entryptr, end_of_buf);
else
len = le32_to_cpu(dir_info->FileNameLength);
if (len < 0 ||
entryptr + len < entryptr ||
entryptr + len > end_of_buf || entryptr + len > end_of_buf ||
entryptr + len + size > end_of_buf) { entryptr + len + size > end_of_buf) {
cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n", cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n",
...@@ -4473,6 +4479,9 @@ int SMB2_query_directory_init(const unsigned int xid, ...@@ -4473,6 +4479,9 @@ int SMB2_query_directory_init(const unsigned int xid,
case SMB_FIND_FILE_ID_FULL_DIR_INFO: case SMB_FIND_FILE_ID_FULL_DIR_INFO:
req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION; req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
break; break;
case SMB_FIND_FILE_POSIX_INFO:
req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO;
break;
default: default:
cifs_tcon_dbg(VFS, "info level %u isn't supported\n", cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
info_level); info_level);
...@@ -4538,6 +4547,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, ...@@ -4538,6 +4547,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
case SMB_FIND_FILE_ID_FULL_DIR_INFO: case SMB_FIND_FILE_ID_FULL_DIR_INFO:
info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1; info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
break; break;
case SMB_FIND_FILE_POSIX_INFO:
/* note that posix payload are variable size */
info_buf_size = sizeof(struct smb2_posix_info);
break;
default: default:
cifs_tcon_dbg(VFS, "info level %u isn't supported\n", cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
srch_inf->info_level); srch_inf->info_level);
...@@ -4547,8 +4560,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, ...@@ -4547,8 +4560,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
le32_to_cpu(rsp->OutputBufferLength), rsp_iov, le32_to_cpu(rsp->OutputBufferLength), rsp_iov,
info_buf_size); info_buf_size);
if (rc) if (rc) {
cifs_tcon_dbg(VFS, "bad info payload");
return rc; return rc;
}
srch_inf->unicode = true; srch_inf->unicode = true;
...@@ -4562,9 +4577,14 @@ smb2_parse_query_directory(struct cifs_tcon *tcon, ...@@ -4562,9 +4577,14 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
srch_inf->srch_entries_start = srch_inf->last_entry = srch_inf->srch_entries_start = srch_inf->last_entry =
(char *)rsp + le16_to_cpu(rsp->OutputBufferOffset); (char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
end_of_smb = rsp_iov->iov_len + (char *)rsp; end_of_smb = rsp_iov->iov_len + (char *)rsp;
srch_inf->entries_in_buffer =
num_entries(srch_inf->srch_entries_start, end_of_smb, srch_inf->entries_in_buffer = num_entries(
&srch_inf->last_entry, info_buf_size); srch_inf->info_level,
srch_inf->srch_entries_start,
end_of_smb,
&srch_inf->last_entry,
info_buf_size);
srch_inf->index_of_last_entry += srch_inf->entries_in_buffer; srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n", cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
srch_inf->entries_in_buffer, srch_inf->index_of_last_entry, srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
......
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