Skip to content

Commit 3d519bd

Browse files
aaptelsmfrench
authored andcommitted
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 <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 349e13a commit 3d519bd

File tree

2 files changed

+109
-7
lines changed

2 files changed

+109
-7
lines changed

fs/cifs/readdir.c

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "cifs_debug.h"
3333
#include "cifs_fs_sb.h"
3434
#include "cifsfs.h"
35+
#include "smb2proto.h"
3536

3637
/*
3738
* 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)
217218
}
218219
}
219220

221+
/* Fill a cifs_fattr struct with info from SMB_FIND_FILE_POSIX_INFO. */
222+
static void
223+
cifs_posix_to_fattr(struct cifs_fattr *fattr, struct smb2_posix_info *info,
224+
struct cifs_sb_info *cifs_sb)
225+
{
226+
struct smb2_posix_info_parsed parsed;
227+
228+
posix_info_parse(info, NULL, &parsed);
229+
230+
memset(fattr, 0, sizeof(*fattr));
231+
fattr->cf_uniqueid = le64_to_cpu(info->Inode);
232+
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
233+
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
234+
235+
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
236+
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
237+
fattr->cf_ctime = cifs_NTtimeToUnix(info->CreationTime);
238+
239+
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
240+
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
241+
242+
/*
243+
* Since we set the inode type below we need to mask off
244+
* to avoid strange results if bits set above.
245+
* XXX: why not make server&client use the type bits?
246+
*/
247+
fattr->cf_mode = le32_to_cpu(info->Mode) & ~S_IFMT;
248+
249+
cifs_dbg(VFS, "XXX dev %d, reparse %d, mode %o",
250+
le32_to_cpu(info->DeviceId),
251+
le32_to_cpu(info->ReparseTag),
252+
le32_to_cpu(info->Mode));
253+
254+
if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
255+
fattr->cf_mode |= S_IFDIR;
256+
fattr->cf_dtype = DT_DIR;
257+
} else {
258+
/*
259+
* mark anything that is not a dir as regular
260+
* file. special files should have the REPARSE
261+
* attribute and will be marked as needing revaluation
262+
*/
263+
fattr->cf_mode |= S_IFREG;
264+
fattr->cf_dtype = DT_REG;
265+
}
266+
267+
if (reparse_file_needs_reval(fattr))
268+
fattr->cf_flags |= CIFS_FATTR_NEED_REVAL;
269+
270+
/* TODO map SIDs */
271+
fattr->cf_uid = cifs_sb->mnt_uid;
272+
fattr->cf_gid = cifs_sb->mnt_gid;
273+
}
274+
220275
static void __dir_info_to_fattr(struct cifs_fattr *fattr, const void *info)
221276
{
222277
const FILE_DIRECTORY_INFO *fi = info;
@@ -359,6 +414,8 @@ initiate_cifs_search(const unsigned int xid, struct file *file)
359414
/* if (cap_unix(tcon->ses) { */
360415
if (tcon->unix_ext)
361416
cifsFile->srch_inf.info_level = SMB_FIND_FILE_UNIX;
417+
else if (tcon->posix_extensions)
418+
cifsFile->srch_inf.info_level = SMB_FIND_FILE_POSIX_INFO;
362419
else if ((tcon->ses->capabilities &
363420
tcon->ses->server->vals->cap_nt_find) == 0) {
364421
cifsFile->srch_inf.info_level = SMB_FIND_FILE_INFO_STANDARD;
@@ -451,6 +508,23 @@ struct cifs_dirent {
451508
u64 ino;
452509
};
453510

511+
static void cifs_fill_dirent_posix(struct cifs_dirent *de,
512+
const struct smb2_posix_info *info)
513+
{
514+
struct smb2_posix_info_parsed parsed;
515+
516+
/* payload should have already been checked at this point */
517+
if (posix_info_parse(info, NULL, &parsed) < 0) {
518+
cifs_dbg(VFS, "invalid POSIX info payload");
519+
return;
520+
}
521+
522+
de->name = parsed.name;
523+
de->namelen = parsed.name_len;
524+
de->resume_key = info->Ignored;
525+
de->ino = le64_to_cpu(info->Inode);
526+
}
527+
454528
static void cifs_fill_dirent_unix(struct cifs_dirent *de,
455529
const FILE_UNIX_INFO *info, bool is_unicode)
456530
{
@@ -511,6 +585,9 @@ static int cifs_fill_dirent(struct cifs_dirent *de, const void *info,
511585
memset(de, 0, sizeof(*de));
512586

513587
switch (level) {
588+
case SMB_FIND_FILE_POSIX_INFO:
589+
cifs_fill_dirent_posix(de, info);
590+
break;
514591
case SMB_FIND_FILE_UNIX:
515592
cifs_fill_dirent_unix(de, info, is_unicode);
516593
break;
@@ -786,6 +863,11 @@ static int cifs_filldir(char *find_entry, struct file *file,
786863
}
787864

788865
switch (file_info->srch_inf.info_level) {
866+
case SMB_FIND_FILE_POSIX_INFO:
867+
cifs_posix_to_fattr(&fattr,
868+
(struct smb2_posix_info *)find_entry,
869+
cifs_sb);
870+
break;
789871
case SMB_FIND_FILE_UNIX:
790872
cifs_unix_basic_to_fattr(&fattr,
791873
&((FILE_UNIX_INFO *)find_entry)->basic,

fs/cifs/smb2pdu.c

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4398,7 +4398,8 @@ static int posix_info_extra_size(const void *beg, const void *end)
43984398
}
43994399

44004400
static unsigned int
4401-
num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
4401+
num_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry,
4402+
size_t size)
44024403
{
44034404
int len;
44044405
unsigned int entrycount = 0;
@@ -4422,8 +4423,13 @@ num_entries(char *bufstart, char *end_of_buf, char **lastentry, size_t size)
44224423
entryptr = entryptr + next_offset;
44234424
dir_info = (FILE_DIRECTORY_INFO *)entryptr;
44244425

4425-
len = le32_to_cpu(dir_info->FileNameLength);
4426-
if (entryptr + len < entryptr ||
4426+
if (infotype == SMB_FIND_FILE_POSIX_INFO)
4427+
len = posix_info_extra_size(entryptr, end_of_buf);
4428+
else
4429+
len = le32_to_cpu(dir_info->FileNameLength);
4430+
4431+
if (len < 0 ||
4432+
entryptr + len < entryptr ||
44274433
entryptr + len > end_of_buf ||
44284434
entryptr + len + size > end_of_buf) {
44294435
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,
44734479
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
44744480
req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
44754481
break;
4482+
case SMB_FIND_FILE_POSIX_INFO:
4483+
req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO;
4484+
break;
44764485
default:
44774486
cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
44784487
info_level);
@@ -4538,6 +4547,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
45384547
case SMB_FIND_FILE_ID_FULL_DIR_INFO:
45394548
info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO) - 1;
45404549
break;
4550+
case SMB_FIND_FILE_POSIX_INFO:
4551+
/* note that posix payload are variable size */
4552+
info_buf_size = sizeof(struct smb2_posix_info);
4553+
break;
45414554
default:
45424555
cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
45434556
srch_inf->info_level);
@@ -4547,8 +4560,10 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
45474560
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
45484561
le32_to_cpu(rsp->OutputBufferLength), rsp_iov,
45494562
info_buf_size);
4550-
if (rc)
4563+
if (rc) {
4564+
cifs_tcon_dbg(VFS, "bad info payload");
45514565
return rc;
4566+
}
45524567

45534568
srch_inf->unicode = true;
45544569

@@ -4562,9 +4577,14 @@ smb2_parse_query_directory(struct cifs_tcon *tcon,
45624577
srch_inf->srch_entries_start = srch_inf->last_entry =
45634578
(char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
45644579
end_of_smb = rsp_iov->iov_len + (char *)rsp;
4565-
srch_inf->entries_in_buffer =
4566-
num_entries(srch_inf->srch_entries_start, end_of_smb,
4567-
&srch_inf->last_entry, info_buf_size);
4580+
4581+
srch_inf->entries_in_buffer = num_entries(
4582+
srch_inf->info_level,
4583+
srch_inf->srch_entries_start,
4584+
end_of_smb,
4585+
&srch_inf->last_entry,
4586+
info_buf_size);
4587+
45684588
srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
45694589
cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
45704590
srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,

0 commit comments

Comments
 (0)