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: Aurelien Aptel <aaptel@suse.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
349e13ad30
commit
3d519bd126
|
@ -32,6 +32,7 @@
|
|||
#include "cifs_debug.h"
|
||||
#include "cifs_fs_sb.h"
|
||||
#include "cifsfs.h"
|
||||
#include "smb2proto.h"
|
||||
|
||||
/*
|
||||
* 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)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
const FILE_DIRECTORY_INFO *fi = info;
|
||||
|
@ -359,6 +414,8 @@ ffirst_retry:
|
|||
/* if (cap_unix(tcon->ses) { */
|
||||
if (tcon->unix_ext)
|
||||
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 &
|
||||
tcon->ses->server->vals->cap_nt_find) == 0) {
|
||||
cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
|
||||
|
@ -451,6 +508,23 @@ struct cifs_dirent {
|
|||
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,
|
||||
const FILE_UNIX_INFO *info, bool is_unicode)
|
||||
{
|
||||
|
@ -511,6 +585,9 @@ static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
|
|||
memset(de, 0, sizeof(*de));
|
||||
|
||||
switch (level) {
|
||||
case SMB_FIND_FILE_POSIX_INFO:
|
||||
cifs_fill_dirent_posix(de, info);
|
||||
break;
|
||||
case SMB_FIND_FILE_UNIX:
|
||||
cifs_fill_dirent_unix(de, info, is_unicode);
|
||||
break;
|
||||
|
@ -786,6 +863,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
|
|||
}
|
||||
|
||||
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:
|
||||
cifs_unix_basic_to_fattr(&fattr,
|
||||
&((FILE_UNIX_INFO *)find_entry)->basic,
|
||||
|
|
|
@ -4398,7 +4398,8 @@ static int posix_info_extra_size(const void *beg, const void *end)
|
|||
}
|
||||
|
||||
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;
|
||||
unsigned int entrycount = 0;
|
||||
|
@ -4422,8 +4423,13 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
|
|||
entryptr = entryptr + next_offset;
|
||||
dir_info = (FILE_DIRECTORY_INFO *)entryptr;
|
||||
|
||||
len = le32_to_cpu(dir_info->FileNameLength);
|
||||
if (entryptr + len < entryptr ||
|
||||
if (infotype == SMB_FIND_FILE_POSIX_INFO)
|
||||
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 + size > end_of_buf) {
|
||||
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,
|
|||
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
||||
req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
|
||||
break;
|
||||
case SMB_FIND_FILE_POSIX_INFO:
|
||||
req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO;
|
||||
break;
|
||||
default:
|
||||
cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
|
||||
info_level);
|
||||
|
@ -4538,6 +4547,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
|
|||
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
|
||||
info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
|
||||
break;
|
||||
case SMB_FIND_FILE_POSIX_INFO:
|
||||
/* note that posix payload are variable size */
|
||||
info_buf_size = sizeof(struct smb2_posix_info);
|
||||
break;
|
||||
default:
|
||||
cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
|
||||
srch_inf->info_level);
|
||||
|
@ -4547,8 +4560,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
|
|||
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
|
||||
le32_to_cpu(rsp->OutputBufferLength), rsp_iov,
|
||||
info_buf_size);
|
||||
if (rc)
|
||||
if (rc) {
|
||||
cifs_tcon_dbg(VFS, "bad info payload");
|
||||
return rc;
|
||||
}
|
||||
|
||||
srch_inf->unicode = true;
|
||||
|
||||
|
@ -4562,9 +4577,14 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
|
|||
srch_inf->srch_entries_start = srch_inf->last_entry =
|
||||
(char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
|
||||
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->last_entry, info_buf_size);
|
||||
|
||||
srch_inf->entries_in_buffer = num_entries(
|
||||
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;
|
||||
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,
|
||||
|
|
Loading…
Reference in New Issue