Skip to content

Commit 106a6a0

Browse files
palijfvogel
authored andcommitted
cifs: Fix and improve cifs_query_path_info() and cifs_query_file_info()
[ Upstream commit 1041c117a2c33cdffc4f695ac4b469e9124d24d5 ] When CAP_NT_SMBS was not negotiated then do not issue CIFSSMBQPathInfo() and CIFSSMBQFileInfo() commands. CIFSSMBQPathInfo() is not supported by non-NT Win9x SMB server and CIFSSMBQFileInfo() returns from Win9x SMB server bogus data in Attributes field (for example lot of files are marked as reparse points, even Win9x does not support them and read-only bit is not marked for read-only files). Correct information is returned by CIFSFindFirst() or SMBQueryInformation() command. So as a fallback in cifs_query_path_info() function use CIFSFindFirst() with SMB_FIND_FILE_FULL_DIRECTORY_INFO level which is supported by both NT and non-NT servers and as a last option use SMBQueryInformation() as it was before. And in function cifs_query_file_info() immediately returns -EOPNOTSUPP when not communicating with NT server. Client then revalidate inode entry by the cifs_query_path_info() call, which is working fine. So fstat() syscall on already opened file will receive correct information. Note that both fallback functions in non-UNICODE mode expands wildcards. Therefore those fallback functions cannot be used on paths which contain SMB wildcard characters (* ? " > <). CIFSFindFirst() returns all 4 time attributes as opposite of SMBQueryInformation() which returns only one. With this change it is possible to query all 4 times attributes from Win9x server and at the same time, client minimize sending of unsupported commands to server. Signed-off-by: Pali Rohár <[email protected]> Signed-off-by: Steve French <[email protected]> Signed-off-by: Sasha Levin <[email protected]> (cherry picked from commit 0a9920e1ff67d17dd605e5ed9cd4778d42e9051e) Signed-off-by: Jack Vogel <[email protected]>
1 parent 1eb2d2a commit 106a6a0

File tree

1 file changed

+95
-8
lines changed

1 file changed

+95
-8
lines changed

fs/smb/client/smb1ops.c

Lines changed: 95 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -541,24 +541,104 @@ static int cifs_query_path_info(const unsigned int xid,
541541
const char *full_path,
542542
struct cifs_open_info_data *data)
543543
{
544-
int rc;
544+
int rc = -EOPNOTSUPP;
545545
FILE_ALL_INFO fi = {};
546+
struct cifs_search_info search_info = {};
547+
bool non_unicode_wildcard = false;
546548

547549
data->reparse_point = false;
548550
data->adjust_tz = false;
549551

550-
/* could do find first instead but this returns more info */
551-
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */, cifs_sb->local_nls,
552-
cifs_remap(cifs_sb));
553552
/*
554-
* BB optimize code so we do not make the above call when server claims
555-
* no NT SMB support and the above call failed at least once - set flag
556-
* in tcon or mount.
553+
* First try CIFSSMBQPathInfo() function which returns more info
554+
* (NumberOfLinks) than CIFSFindFirst() fallback function.
555+
* Some servers like Win9x do not support SMB_QUERY_FILE_ALL_INFO over
556+
* TRANS2_QUERY_PATH_INFORMATION, but supports it with filehandle over
557+
* TRANS2_QUERY_FILE_INFORMATION (function CIFSSMBQFileInfo(). But SMB
558+
* Open command on non-NT servers works only for files, does not work
559+
* for directories. And moreover Win9x SMB server returns bogus data in
560+
* SMB_QUERY_FILE_ALL_INFO Attributes field. So for non-NT servers,
561+
* do not even use CIFSSMBQPathInfo() or CIFSSMBQFileInfo() function.
562+
*/
563+
if (tcon->ses->capabilities & CAP_NT_SMBS)
564+
rc = CIFSSMBQPathInfo(xid, tcon, full_path, &fi, 0 /* not legacy */,
565+
cifs_sb->local_nls, cifs_remap(cifs_sb));
566+
567+
/*
568+
* Non-UNICODE variant of fallback functions below expands wildcards,
569+
* so they cannot be used for querying paths with wildcard characters.
557570
*/
558-
if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) {
571+
if (rc && !(tcon->ses->capabilities & CAP_UNICODE) && strpbrk(full_path, "*?\"><"))
572+
non_unicode_wildcard = true;
573+
574+
/*
575+
* Then fallback to CIFSFindFirst() which works also with non-NT servers
576+
* but does not does not provide NumberOfLinks.
577+
*/
578+
if ((rc == -EOPNOTSUPP || rc == -EINVAL) &&
579+
!non_unicode_wildcard) {
580+
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find))
581+
search_info.info_level = SMB_FIND_FILE_INFO_STANDARD;
582+
else
583+
search_info.info_level = SMB_FIND_FILE_FULL_DIRECTORY_INFO;
584+
rc = CIFSFindFirst(xid, tcon, full_path, cifs_sb, NULL,
585+
CIFS_SEARCH_CLOSE_ALWAYS | CIFS_SEARCH_CLOSE_AT_END,
586+
&search_info, false);
587+
if (rc == 0) {
588+
if (!(tcon->ses->capabilities & tcon->ses->server->vals->cap_nt_find)) {
589+
FIND_FILE_STANDARD_INFO *di;
590+
int offset = tcon->ses->server->timeAdj;
591+
592+
di = (FIND_FILE_STANDARD_INFO *)search_info.srch_entries_start;
593+
fi.CreationTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
594+
di->CreationDate, di->CreationTime, offset)));
595+
fi.LastAccessTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
596+
di->LastAccessDate, di->LastAccessTime, offset)));
597+
fi.LastWriteTime = cpu_to_le64(cifs_UnixTimeToNT(cnvrtDosUnixTm(
598+
di->LastWriteDate, di->LastWriteTime, offset)));
599+
fi.ChangeTime = fi.LastWriteTime;
600+
fi.Attributes = cpu_to_le32(le16_to_cpu(di->Attributes));
601+
fi.AllocationSize = cpu_to_le64(le32_to_cpu(di->AllocationSize));
602+
fi.EndOfFile = cpu_to_le64(le32_to_cpu(di->DataSize));
603+
} else {
604+
FILE_FULL_DIRECTORY_INFO *di;
605+
606+
di = (FILE_FULL_DIRECTORY_INFO *)search_info.srch_entries_start;
607+
fi.CreationTime = di->CreationTime;
608+
fi.LastAccessTime = di->LastAccessTime;
609+
fi.LastWriteTime = di->LastWriteTime;
610+
fi.ChangeTime = di->ChangeTime;
611+
fi.Attributes = di->ExtFileAttributes;
612+
fi.AllocationSize = di->AllocationSize;
613+
fi.EndOfFile = di->EndOfFile;
614+
fi.EASize = di->EaSize;
615+
}
616+
fi.NumberOfLinks = cpu_to_le32(1);
617+
fi.DeletePending = 0;
618+
fi.Directory = !!(le32_to_cpu(fi.Attributes) & ATTR_DIRECTORY);
619+
cifs_buf_release(search_info.ntwrk_buf_start);
620+
} else if (!full_path[0]) {
621+
/*
622+
* CIFSFindFirst() does not work on root path if the
623+
* root path was exported on the server from the top
624+
* level path (drive letter).
625+
*/
626+
rc = -EOPNOTSUPP;
627+
}
628+
}
629+
630+
/*
631+
* If everything failed then fallback to the legacy SMB command
632+
* SMB_COM_QUERY_INFORMATION which works with all servers, but
633+
* provide just few information.
634+
*/
635+
if ((rc == -EOPNOTSUPP || rc == -EINVAL) && !non_unicode_wildcard) {
559636
rc = SMBQueryInformation(xid, tcon, full_path, &fi, cifs_sb->local_nls,
560637
cifs_remap(cifs_sb));
561638
data->adjust_tz = true;
639+
} else if ((rc == -EOPNOTSUPP || rc == -EINVAL) && non_unicode_wildcard) {
640+
/* Path with non-UNICODE wildcard character cannot exist. */
641+
rc = -ENOENT;
562642
}
563643

564644
if (!rc) {
@@ -655,6 +735,13 @@ static int cifs_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
655735
int rc;
656736
FILE_ALL_INFO fi = {};
657737

738+
/*
739+
* CIFSSMBQFileInfo() for non-NT servers returns bogus data in
740+
* Attributes fields. So do not use this command for non-NT servers.
741+
*/
742+
if (!(tcon->ses->capabilities & CAP_NT_SMBS))
743+
return -EOPNOTSUPP;
744+
658745
if (cfile->symlink_target) {
659746
data->symlink_target = kstrdup(cfile->symlink_target, GFP_KERNEL);
660747
if (!data->symlink_target)

0 commit comments

Comments
 (0)