diff -Naup -x '*.o' -x '*.ko' -x '*.mod*' cifs.orig/cifsglob.h cifs.patched/cifsglob.h --- cifs.orig/cifsglob.h 2014-04-27 02:19:26.000000000 +0200 +++ cifs.patched/cifsglob.h 2014-05-04 17:27:46.000000000 +0200 @@ -866,6 +866,7 @@ struct cifs_tcon { bool local_lease:1; /* check leases (only) on local system not remote */ bool broken_posix_open; /* e.g. Samba server versions < 3.3.2, 3.2.9 */ bool need_reconnect:1; /* connection reset, tid now invalid */ + bool broken_qpath_info:1; #ifdef CONFIG_CIFS_SMB2 bool print:1; /* set if connection to printer share */ bool bad_network_name:1; /* set if ret status STATUS_BAD_NETWORK_NAME */ diff -Naup -x '*.o' -x '*.ko' -x '*.mod*' cifs.orig/cifspdu.h cifs.patched/cifspdu.h --- cifs.orig/cifspdu.h 2014-04-27 02:19:26.000000000 +0200 +++ cifs.patched/cifspdu.h 2014-05-04 17:27:46.000000000 +0200 @@ -2287,6 +2287,16 @@ typedef struct { /* data block encoding char FileName[1]; } __attribute__((packed)) FILE_ALL_INFO; /* level 0x107 QPathInfo */ +typedef struct { + __le64 AllocationSize; + __le64 EndOfFile; /* size ie offset to first free byte in file */ + __le32 NumberOfLinks; /* hard links */ + __u8 DeletePending; + __u8 Directory; + __u16 Pad; +} __attribute__((packed)) FILE_STANDARD_INFO; /* level 0x102 QPathInfo */ + + /* defines for enumerating possible values of the Unix type field below */ #define UNIX_FILE 0 #define UNIX_DIR 1 diff -Naup -x '*.o' -x '*.ko' -x '*.mod*' cifs.orig/cifsproto.h cifs.patched/cifsproto.h --- cifs.orig/cifsproto.h 2014-04-27 02:19:26.000000000 +0200 +++ cifs.patched/cifsproto.h 2014-05-04 17:27:46.000000000 +0200 @@ -242,6 +242,12 @@ extern int CIFSSMBQPathInfo(const unsign const char *search_Name, FILE_ALL_INFO *data, int legacy /* whether to use old info level */, const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBQPathInfoBasic(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, int remap); +extern int CIFSSMBQPathInfoStandard(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_STANDARD_INFO *data, + const struct nls_table *nls_codepage, int remap); extern int SMBQueryInformation(const unsigned int xid, struct cifs_tcon *tcon, const char *search_name, FILE_ALL_INFO *data, const struct nls_table *nls_codepage, int remap); diff -Naup -x '*.o' -x '*.ko' -x '*.mod*' cifs.orig/cifssmb.c cifs.patched/cifssmb.c --- cifs.orig/cifssmb.c 2014-04-27 02:19:26.000000000 +0200 +++ cifs.patched/cifssmb.c 2014-05-04 17:38:41.000000000 +0200 @@ -4048,13 +4048,13 @@ QFileInfoRetry: return rc; } -int -CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, - const char *search_name, FILE_ALL_INFO *data, - int legacy /* old style infolevel */, - const struct nls_table *nls_codepage, int remap) +static int +CIFSSMBQPathInfoImpl(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, + void *data, int size, + __u16 level, __u16 bcc, + const struct nls_table *nls_codepage, int remap) { - /* level 263 SMB_QUERY_FILE_ALL_INFO */ TRANSACTION2_QPI_REQ *pSMB = NULL; TRANSACTION2_QPI_RSP *pSMBr = NULL; int rc = 0; @@ -4062,7 +4062,7 @@ CIFSSMBQPathInfo(const unsigned int xid, int name_len; __u16 params, byte_count; - /* cifs_dbg(FYI, "In QPathInfo path %s\n", search_name); */ + cifs_dbg(FYI, "In QPathInfo level %u path %s", level, search_name); QPathInfoRetry: rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB, (void **) &pSMBr); @@ -4101,10 +4101,7 @@ QPathInfoRetry: byte_count = params + 1 /* pad */ ; pSMB->TotalParameterCount = cpu_to_le16(params); pSMB->ParameterCount = pSMB->TotalParameterCount; - if (legacy) - pSMB->InformationLevel = cpu_to_le16(SMB_INFO_STANDARD); - else - pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_ALL_INFO); + pSMB->InformationLevel = level; pSMB->Reserved4 = 0; inc_rfc1001_len(pSMB, byte_count); pSMB->ByteCount = cpu_to_le16(byte_count); @@ -4112,31 +4109,16 @@ QPathInfoRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { - cifs_dbg(FYI, "Send error in QPathInfo = %d\n", rc); + cifs_dbg(FYI, "Send error in UnixQFileInfo = %d", rc); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); if (rc) /* BB add auto retry on EOPNOTSUPP? */ rc = -EIO; - else if (!legacy && get_bcc(&pSMBr->hdr) < 40) + else if (get_bcc(&pSMBr->hdr) < bcc) rc = -EIO; /* bad smb */ - else if (legacy && get_bcc(&pSMBr->hdr) < 24) - rc = -EIO; /* 24 or 26 expected but we do not read - last field */ else if (data) { - int size; __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset); - - /* - * On legacy responses we do not read the last field, - * EAsize, fortunately since it varies by subdialect and - * also note it differs on Set vs Get, ie two bytes or 4 - * bytes depending but we don't care here. - */ - if (legacy) - size = sizeof(FILE_INFO_STANDARD); - else - size = sizeof(FILE_ALL_INFO); memcpy((char *) data, (char *) &pSMBr->hdr.Protocol + data_offset, size); } else @@ -4150,6 +4132,51 @@ QPathInfoRetry: } int +CIFSSMBQPathInfo(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_ALL_INFO *data, + int legacy /* old style infolevel */, + const struct nls_table *nls_codepage, int remap) +{ + if (legacy) { + /* 24 or 26 expected but on legacy responses we do not read the + last field, EAsize, fortunately since it varies by subdialect + and also note it differs on Set vs. Get, ie two bytes or 4 + bytes depending but we don't care here */ + return CIFSSMBQPathInfoImpl(xid, tcon, search_name, + data, sizeof(FILE_INFO_STANDARD), + SMB_INFO_STANDARD, 24, + nls_codepage, remap); + } else { + return CIFSSMBQPathInfoImpl(xid, tcon, search_name, + data, sizeof(FILE_ALL_INFO), + SMB_QUERY_FILE_ALL_INFO, 40, + nls_codepage, remap); + } +} + +int +CIFSSMBQPathInfoBasic(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_BASIC_INFO *data, + const struct nls_table *nls_codepage, int remap) +{ + return CIFSSMBQPathInfoImpl(xid, tcon, search_name, + data, sizeof(FILE_BASIC_INFO), + SMB_QUERY_FILE_BASIC_INFO, 40 /*???*/, + nls_codepage, remap); +} + +int +CIFSSMBQPathInfoStandard(const unsigned int xid, struct cifs_tcon *tcon, + const char *search_name, FILE_STANDARD_INFO *data, + const struct nls_table *nls_codepage, int remap) +{ + return CIFSSMBQPathInfoImpl(xid, tcon, search_name, + data, sizeof(FILE_STANDARD_INFO), + SMB_QUERY_FILE_STANDARD_INFO, 24 /*???*/, + nls_codepage, remap); +} + +int CIFSSMBUnixQFileInfo(const unsigned int xid, struct cifs_tcon *tcon, u16 netfid, FILE_UNIX_BASIC_INFO *pFindData) { @@ -4194,7 +4221,7 @@ UnixQFileInfoRetry: rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, (struct smb_hdr *) pSMBr, &bytes_returned, 0); if (rc) { - cifs_dbg(FYI, "Send error in UnixQFileInfo = %d", rc); + cifs_dbg(FYI, "Send error in UnixQPathInfo = %d", rc); } else { /* decode response */ rc = validate_t2((struct smb_t2_rsp *)pSMBr); diff -Naup -x '*.o' -x '*.ko' -x '*.mod*' cifs.orig/smb1ops.c cifs.patched/smb1ops.c --- cifs.orig/smb1ops.c 2014-04-27 02:19:26.000000000 +0200 +++ cifs.patched/smb1ops.c 2014-05-04 17:27:46.000000000 +0200 @@ -511,22 +511,31 @@ static int cifs_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon, struct cifs_sb_info *cifs_sb, const char *full_path) { - int rc; + int rc= -EIO; FILE_ALL_INFO *file_info; + const int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; file_info = kmalloc(sizeof(FILE_ALL_INFO), GFP_KERNEL); if (file_info == NULL) return -ENOMEM; - rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, - 0 /* not legacy */, cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + if (!tcon->broken_qpath_info) { + rc = CIFSSMBQPathInfo(xid, tcon, full_path, file_info, + 0, cifs_sb->local_nls, remap); + } + if (rc == -EIO) { + rc = CIFSSMBQPathInfoBasic(xid, tcon, full_path, + (FILE_BASIC_INFO*)file_info, + cifs_sb->local_nls, remap); + cifs_dbg(FYI, "is_path_accessible: FALLBACK returns %d", rc); + if (!rc) { + tcon->broken_qpath_info = true; + } + } if (rc == -EOPNOTSUPP || rc == -EINVAL) rc = SMBQueryInformation(xid, tcon, full_path, file_info, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_sb->local_nls, remap); kfree(file_info); return rc; } @@ -536,14 +545,32 @@ cifs_query_path_info(const unsigned int struct cifs_sb_info *cifs_sb, const char *full_path, FILE_ALL_INFO *data, bool *adjustTZ, bool *symlink) { - int rc; + int rc = -EIO; + const int remap = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MAP_SPECIAL_CHR; *symlink = false; /* could do find first instead but this returns more info */ - rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0 /* not legacy */, - cifs_sb->local_nls, cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + if (!tcon->broken_qpath_info) { + rc = CIFSSMBQPathInfo(xid, tcon, full_path, data, 0, + cifs_sb->local_nls, remap); + } + + if (rc == -EIO) { + rc = CIFSSMBQPathInfoBasic(xid, tcon, full_path, + (FILE_BASIC_INFO*)data, + cifs_sb->local_nls, remap); + if (!rc) { + rc = CIFSSMBQPathInfoStandard(xid, tcon, full_path, + (void*)data + sizeof(FILE_BASIC_INFO), + cifs_sb->local_nls, remap); + } + cifs_dbg(FYI, "cifs_query_path_info: FALLBACK returns %d", rc); + if (!rc) { + tcon->broken_qpath_info = true; + } + } + /* * BB optimize code so we do not make the above call when server claims * no NT SMB support and the above call failed at least once - set flag @@ -551,9 +578,7 @@ cifs_query_path_info(const unsigned int */ if ((rc == -EOPNOTSUPP) || (rc == -EINVAL)) { rc = SMBQueryInformation(xid, tcon, full_path, data, - cifs_sb->local_nls, - cifs_sb->mnt_cifs_flags & - CIFS_MOUNT_MAP_SPECIAL_CHR); + cifs_sb->local_nls, remap); *adjustTZ = true; }