diff mbox

cifs: obtain file access during backup intent lookup

Message ID 1341926028-30845-1-git-send-email-shirishpargaonkar@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Shirish Pargaonkar July 10, 2012, 1:13 p.m. UTC
From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>


path based querries can fail for lack of access, especially during lookup
during open.
open itself would actually succeed becasue of back up intent bit
but querries (either path or file handle based) do not have a means to 
specifiy backup intent bit.
So querry the file info during lookup using a file handle (based call)
obtained by opening the file only for that purpose (and then closing the file).
 
Add a file handle based call to obtain Index Number of a file
in addition to an existing path based call.

Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
Reported-by: Tushar Gosavi <tugosavi@in.ibm.com>
---
 fs/cifs/cifsproto.h |    4 ++
 fs/cifs/cifssmb.c   |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/inode.c     |   31 ++++++++++++++++++-
 3 files changed, 113 insertions(+), 2 deletions(-)

Comments

Pavel Shilovsky July 12, 2012, 1:11 p.m. UTC | #1
2012/7/10  <shirishpargaonkar@gmail.com>:
> From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>
>
> path based querries can fail for lack of access, especially during lookup
> during open.
> open itself would actually succeed becasue of back up intent bit
> but querries (either path or file handle based) do not have a means to
> specifiy backup intent bit.
> So querry the file info during lookup using a file handle (based call)
> obtained by opening the file only for that purpose (and then closing the file).
>
> Add a file handle based call to obtain Index Number of a file
> in addition to an existing path based call.
>
> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
> Reported-by: Tushar Gosavi <tugosavi@in.ibm.com>
> ---
>  fs/cifs/cifsproto.h |    4 ++
>  fs/cifs/cifssmb.c   |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/inode.c     |   31 ++++++++++++++++++-
>  3 files changed, 113 insertions(+), 2 deletions(-)
>
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 0a6cbfe..3c63ee4 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -375,6 +375,10 @@ extern int CIFSGetSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
>                         const unsigned char *searchName, __u64 *inode_number,
>                         const struct nls_table *nls_codepage,
>                         int remap_special_chars);
> +extern int CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
> +                       u16 netfid, __u64 *inode_number,
> +                       const struct nls_table *nls_codepage,
> +                       int remap_special_chars);
>
>  extern int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
>                       const __u8 lock_type, const __u32 num_unlock,
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 5b40073..dcd4091 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -4621,6 +4621,86 @@ GetInodeNumOut:
>         return rc;
>  }
>
> +int
> +CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
> +                     u16 netfid, __u64 *inode_number,
> +                     const struct nls_table *nls_codepage, int remap)
> +{
> +       int rc = 0;
> +       struct smb_t2_qfi_req *pSMB = NULL;
> +       struct smb_t2_qfi_rsp *pSMBr = NULL;

While the existing code uses the same "pSMB" names I suggest to follow
kernel coding style for a new code. May be something like "req" and
"rsp" more suitable?

> +       int bytes_returned;
> +       __u16 params, byte_count;
> +
> +       cFYI(1, "In %s", __func__);
> +       if (tcon == NULL)
> +               return -ENODEV;
> +
> +FileGetInodeNumberRetry:
> +       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
> +                     (void **) &pSMBr);
> +       if (rc)
> +               return rc;
> +
> +       params = 2 /* level */  + 2 /* fid */;
> +       pSMB->t2.TotalDataCount = 0;
> +       pSMB->t2.MaxParameterCount = cpu_to_le16(4);
> +       /* BB find exact max data count below from sess structure BB */
> +       pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
> +       pSMB->t2.MaxSetupCount = 0;
> +       pSMB->t2.Reserved = 0;
> +       pSMB->t2.Flags = 0;
> +       pSMB->t2.Timeout = 0;
> +       pSMB->t2.Reserved2 = 0;
> +       pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(
> +               struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
> +       pSMB->t2.DataCount = 0;
> +       pSMB->t2.DataOffset = 0;
> +       pSMB->t2.SetupCount = 1;
> +       pSMB->t2.Reserved3 = 0;
> +       pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
> +       byte_count = params + 1 /* pad */ ;
> +       pSMB->t2.TotalParameterCount = cpu_to_le16(params);
> +       pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
> +       pSMB->Fid = cpu_to_le16(netfid);
> +       pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
> +       inc_rfc1001_len(pSMB, byte_count);
> +       pSMB->t2.ByteCount = cpu_to_le16(byte_count);
> +
> +       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
> +               (struct smb_hdr *) pSMBr, &bytes_returned, 0);
> +       if (rc) {
> +               cFYI(1, "error %d in QueryInternalInfo", rc);
> +       } else {
> +               /* decode response */
> +               rc = validate_t2((struct smb_t2_rsp *)pSMBr);
> +               /* BB also check enough total bytes returned */
> +               if (rc || get_bcc(&pSMBr->hdr) < 2)
> +                       /* If rc should we check for EOPNOSUPP and
> +                       disable the srvino flag? or in caller? */
> +                       rc = -EIO;      /* bad smb */
> +               else {
> +                       __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
> +                       __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
> +                       struct file_internal_info *pfinfo;
> +                       /* BB Do we need a cast or hash here ? */
> +                       if (count < 8) {
> +                               cFYI(1, "Illegal size ret in FileQryIntrnlInf");
> +                               rc = -EIO;
> +                               goto FileGetInodeNumOut;
> +                       }
> +                       pfinfo = (struct file_internal_info *)
> +                               (data_offset + (char *) &pSMBr->hdr.Protocol);
> +                       *inode_number = le64_to_cpu(pfinfo->UniqueId);
> +               }
> +       }
> +FileGetInodeNumOut:
> +       cifs_buf_release(pSMB);
> +       if (rc == -EAGAIN)
> +               goto FileGetInodeNumberRetry;
> +       return rc;
> +}
> +
>  /* parses DFS refferal V3 structure
>   * caller is responsible for freeing target_nodes
>   * returns:
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 745da3d..a7c07a0 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -604,7 +604,8 @@ int cifs_get_inode_info(struct inode **pinode,
>         const unsigned char *full_path, FILE_ALL_INFO *pfindData,
>         struct super_block *sb, int xid, const __u16 *pfid)
>  {
> -       int rc = 0, tmprc;
> +       __u16 fid;
> +       int rc = 0, rc2 = -EINVAL, tmprc, oplock = 0;
>         struct cifs_tcon *pTcon;
>         struct tcon_link *tlink;
>         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
> @@ -640,6 +641,23 @@ int cifs_get_inode_info(struct inode **pinode,
>                               0 /* not legacy */,
>                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
>                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
> +
> +               /*
> +                * This sequence of calls is meant for a lookup operation
> +                * that would otherwise fail since query path info has no
> +                * means to indicate backup intent.
> +                */
> +               if (rc == -EACCES && *pinode == NULL && backup_cred(cifs_sb)) {
> +                       /* pfindData contains same info returned by qpathinfo */
> +                       rc2 = CIFSSMBOpen(xid, pTcon, full_path,
> +                               FILE_OPEN, GENERIC_READ,
> +                               CREATE_OPEN_BACKUP_INTENT, &fid, &oplock,
> +                               pfindData, cifs_sb->local_nls,
> +                               cifs_sb->mnt_cifs_flags &
> +                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
> +                       rc = rc2;
> +               }
> +
>                 /* 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 in tcon or mount */
> @@ -683,7 +701,14 @@ int cifs_get_inode_info(struct inode **pinode,
>                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
>                         int rc1 = 0;
>
> -                       rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
> +                       if (!rc2)
> +                               rc1 = CIFSGetFileSrvInodeNumber(xid, pTcon,
> +                                       fid, &fattr.cf_uniqueid,
> +                                       cifs_sb->local_nls,
> +                                       cifs_sb->mnt_cifs_flags &
> +                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
> +                       else
> +                               rc1 = CIFSGetSrvInodeNumber(xid, pTcon,

This will cause merge conflicts with my smb2 changes - but I am not
sure what Steve prefer to merge at first.

>                                         full_path, &fattr.cf_uniqueid,
>                                         cifs_sb->local_nls,
>                                         cifs_sb->mnt_cifs_flags &
> @@ -699,6 +724,8 @@ int cifs_get_inode_info(struct inode **pinode,
>         } else {
>                 fattr.cf_uniqueid = CIFS_I(*pinode)->uniqueid;
>         }
> +       if (!rc2)
> +               CIFSSMBClose(xid, pTcon, fid);
>
>         /* query for SFU type info if supported and needed */
>         if (fattr.cf_cifsattrs & ATTR_SYSTEM &&
> --
> 1.6.0.2
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Steve French July 12, 2012, 3:04 p.m. UTC | #2
On Thu, Jul 12, 2012 at 6:11 AM, Pavel Shilovsky <piastryyy@gmail.com> wrote:
> 2012/7/10  <shirishpargaonkar@gmail.com>:
>> From: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>>
>>
>> path based querries can fail for lack of access, especially during lookup
>> during open.
>> open itself would actually succeed becasue of back up intent bit
>> but querries (either path or file handle based) do not have a means to
>> specifiy backup intent bit.
>> So querry the file info during lookup using a file handle (based call)
>> obtained by opening the file only for that purpose (and then closing the file).
>>
>> Add a file handle based call to obtain Index Number of a file
>> in addition to an existing path based call.
>>
>> Signed-off-by: Shirish Pargaonkar <shirishpargaonkar@gmail.com>
>> Reported-by: Tushar Gosavi <tugosavi@in.ibm.com>
>> ---
>>  fs/cifs/cifsproto.h |    4 ++
>>  fs/cifs/cifssmb.c   |   80 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>  fs/cifs/inode.c     |   31 ++++++++++++++++++-
>>  3 files changed, 113 insertions(+), 2 deletions(-)
>>
>> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
>> index 0a6cbfe..3c63ee4 100644
>> --- a/fs/cifs/cifsproto.h
>> +++ b/fs/cifs/cifsproto.h
>> @@ -375,6 +375,10 @@ extern int CIFSGetSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
>>                         const unsigned char *searchName, __u64 *inode_number,
>>                         const struct nls_table *nls_codepage,
>>                         int remap_special_chars);
>> +extern int CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
>> +                       u16 netfid, __u64 *inode_number,
>> +                       const struct nls_table *nls_codepage,
>> +                       int remap_special_chars);
>>
>>  extern int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
>>                       const __u8 lock_type, const __u32 num_unlock,
>> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
>> index 5b40073..dcd4091 100644
>> --- a/fs/cifs/cifssmb.c
>> +++ b/fs/cifs/cifssmb.c
>> @@ -4621,6 +4621,86 @@ GetInodeNumOut:
>>         return rc;
>>  }
>>
>> +int
>> +CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
>> +                     u16 netfid, __u64 *inode_number,
>> +                     const struct nls_table *nls_codepage, int remap)
>> +{
>> +       int rc = 0;
>> +       struct smb_t2_qfi_req *pSMB = NULL;
>> +       struct smb_t2_qfi_rsp *pSMBr = NULL;
>
> While the existing code uses the same "pSMB" names I suggest to follow
> kernel coding style for a new code. May be something like "req" and
> "rsp" more suitable?
>
>> +       int bytes_returned;
>> +       __u16 params, byte_count;
>> +
>> +       cFYI(1, "In %s", __func__);
>> +       if (tcon == NULL)
>> +               return -ENODEV;
>> +
>> +FileGetInodeNumberRetry:
>> +       rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
>> +                     (void **) &pSMBr);
>> +       if (rc)
>> +               return rc;
>> +
>> +       params = 2 /* level */  + 2 /* fid */;
>> +       pSMB->t2.TotalDataCount = 0;
>> +       pSMB->t2.MaxParameterCount = cpu_to_le16(4);
>> +       /* BB find exact max data count below from sess structure BB */
>> +       pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
>> +       pSMB->t2.MaxSetupCount = 0;
>> +       pSMB->t2.Reserved = 0;
>> +       pSMB->t2.Flags = 0;
>> +       pSMB->t2.Timeout = 0;
>> +       pSMB->t2.Reserved2 = 0;
>> +       pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(
>> +               struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
>> +       pSMB->t2.DataCount = 0;
>> +       pSMB->t2.DataOffset = 0;
>> +       pSMB->t2.SetupCount = 1;
>> +       pSMB->t2.Reserved3 = 0;
>> +       pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
>> +       byte_count = params + 1 /* pad */ ;
>> +       pSMB->t2.TotalParameterCount = cpu_to_le16(params);
>> +       pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
>> +       pSMB->Fid = cpu_to_le16(netfid);
>> +       pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
>> +       inc_rfc1001_len(pSMB, byte_count);
>> +       pSMB->t2.ByteCount = cpu_to_le16(byte_count);
>> +
>> +       rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
>> +               (struct smb_hdr *) pSMBr, &bytes_returned, 0);
>> +       if (rc) {
>> +               cFYI(1, "error %d in QueryInternalInfo", rc);
>> +       } else {
>> +               /* decode response */
>> +               rc = validate_t2((struct smb_t2_rsp *)pSMBr);
>> +               /* BB also check enough total bytes returned */
>> +               if (rc || get_bcc(&pSMBr->hdr) < 2)
>> +                       /* If rc should we check for EOPNOSUPP and
>> +                       disable the srvino flag? or in caller? */
>> +                       rc = -EIO;      /* bad smb */
>> +               else {
>> +                       __u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
>> +                       __u16 count = le16_to_cpu(pSMBr->t2.DataCount);
>> +                       struct file_internal_info *pfinfo;
>> +                       /* BB Do we need a cast or hash here ? */
>> +                       if (count < 8) {
>> +                               cFYI(1, "Illegal size ret in FileQryIntrnlInf");
>> +                               rc = -EIO;
>> +                               goto FileGetInodeNumOut;
>> +                       }
>> +                       pfinfo = (struct file_internal_info *)
>> +                               (data_offset + (char *) &pSMBr->hdr.Protocol);
>> +                       *inode_number = le64_to_cpu(pfinfo->UniqueId);
>> +               }
>> +       }
>> +FileGetInodeNumOut:
>> +       cifs_buf_release(pSMB);
>> +       if (rc == -EAGAIN)
>> +               goto FileGetInodeNumberRetry;
>> +       return rc;
>> +}
>> +
>>  /* parses DFS refferal V3 structure
>>   * caller is responsible for freeing target_nodes
>>   * returns:
>> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
>> index 745da3d..a7c07a0 100644
>> --- a/fs/cifs/inode.c
>> +++ b/fs/cifs/inode.c
>> @@ -604,7 +604,8 @@ int cifs_get_inode_info(struct inode **pinode,
>>         const unsigned char *full_path, FILE_ALL_INFO *pfindData,
>>         struct super_block *sb, int xid, const __u16 *pfid)
>>  {
>> -       int rc = 0, tmprc;
>> +       __u16 fid;
>> +       int rc = 0, rc2 = -EINVAL, tmprc, oplock = 0;
>>         struct cifs_tcon *pTcon;
>>         struct tcon_link *tlink;
>>         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
>> @@ -640,6 +641,23 @@ int cifs_get_inode_info(struct inode **pinode,
>>                               0 /* not legacy */,
>>                               cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
>>                                 CIFS_MOUNT_MAP_SPECIAL_CHR);
>> +
>> +               /*
>> +                * This sequence of calls is meant for a lookup operation
>> +                * that would otherwise fail since query path info has no
>> +                * means to indicate backup intent.
>> +                */
>> +               if (rc == -EACCES && *pinode == NULL && backup_cred(cifs_sb)) {
>> +                       /* pfindData contains same info returned by qpathinfo */
>> +                       rc2 = CIFSSMBOpen(xid, pTcon, full_path,
>> +                               FILE_OPEN, GENERIC_READ,
>> +                               CREATE_OPEN_BACKUP_INTENT, &fid, &oplock,
>> +                               pfindData, cifs_sb->local_nls,
>> +                               cifs_sb->mnt_cifs_flags &
>> +                                       CIFS_MOUNT_MAP_SPECIAL_CHR);
>> +                       rc = rc2;
>> +               }
>> +
>>                 /* 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 in tcon or mount */
>> @@ -683,7 +701,14 @@ int cifs_get_inode_info(struct inode **pinode,
>>                 if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
>>                         int rc1 = 0;
>>
>> -                       rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
>> +                       if (!rc2)
>> +                               rc1 = CIFSGetFileSrvInodeNumber(xid, pTcon,
>> +                                       fid, &fattr.cf_uniqueid,
>> +                                       cifs_sb->local_nls,
>> +                                       cifs_sb->mnt_cifs_flags &
>> +                                               CIFS_MOUNT_MAP_SPECIAL_CHR);
>> +                       else
>> +                               rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
>
> This will cause merge conflicts with my smb2 changes - but I am not
> sure what Steve prefer to merge at first.

Presumably the backup intent patches should go first (if order matters
due to merge conflict) since they fix an existing problem, but I don't
have a strong preference.
diff mbox

Patch

diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 0a6cbfe..3c63ee4 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -375,6 +375,10 @@  extern int CIFSGetSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
 			const unsigned char *searchName, __u64 *inode_number,
 			const struct nls_table *nls_codepage,
 			int remap_special_chars);
+extern int CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
+			u16 netfid, __u64 *inode_number,
+			const struct nls_table *nls_codepage,
+			int remap_special_chars);
 
 extern int cifs_lockv(const int xid, struct cifs_tcon *tcon, const __u16 netfid,
 		      const __u8 lock_type, const __u32 num_unlock,
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 5b40073..dcd4091 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -4621,6 +4621,86 @@  GetInodeNumOut:
 	return rc;
 }
 
+int
+CIFSGetFileSrvInodeNumber(const int xid, struct cifs_tcon *tcon,
+		      u16 netfid, __u64 *inode_number,
+		      const struct nls_table *nls_codepage, int remap)
+{
+	int rc = 0;
+	struct smb_t2_qfi_req *pSMB = NULL;
+	struct smb_t2_qfi_rsp *pSMBr = NULL;
+	int bytes_returned;
+	__u16 params, byte_count;
+
+	cFYI(1, "In %s", __func__);
+	if (tcon == NULL)
+		return -ENODEV;
+
+FileGetInodeNumberRetry:
+	rc = smb_init(SMB_COM_TRANSACTION2, 15, tcon, (void **) &pSMB,
+		      (void **) &pSMBr);
+	if (rc)
+		return rc;
+
+	params = 2 /* level */  + 2 /* fid */;
+	pSMB->t2.TotalDataCount = 0;
+	pSMB->t2.MaxParameterCount = cpu_to_le16(4);
+	/* BB find exact max data count below from sess structure BB */
+	pSMB->t2.MaxDataCount = cpu_to_le16(CIFSMaxBufSize);
+	pSMB->t2.MaxSetupCount = 0;
+	pSMB->t2.Reserved = 0;
+	pSMB->t2.Flags = 0;
+	pSMB->t2.Timeout = 0;
+	pSMB->t2.Reserved2 = 0;
+	pSMB->t2.ParameterOffset = cpu_to_le16(offsetof(
+		struct smb_com_transaction2_qpi_req, InformationLevel) - 4);
+	pSMB->t2.DataCount = 0;
+	pSMB->t2.DataOffset = 0;
+	pSMB->t2.SetupCount = 1;
+	pSMB->t2.Reserved3 = 0;
+	pSMB->t2.SubCommand = cpu_to_le16(TRANS2_QUERY_FILE_INFORMATION);
+	byte_count = params + 1 /* pad */ ;
+	pSMB->t2.TotalParameterCount = cpu_to_le16(params);
+	pSMB->t2.ParameterCount = pSMB->t2.TotalParameterCount;
+	pSMB->Fid = cpu_to_le16(netfid);
+	pSMB->InformationLevel = cpu_to_le16(SMB_QUERY_FILE_INTERNAL_INFO);
+	inc_rfc1001_len(pSMB, byte_count);
+	pSMB->t2.ByteCount = cpu_to_le16(byte_count);
+
+	rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB,
+		(struct smb_hdr *) pSMBr, &bytes_returned, 0);
+	if (rc) {
+		cFYI(1, "error %d in QueryInternalInfo", rc);
+	} else {
+		/* decode response */
+		rc = validate_t2((struct smb_t2_rsp *)pSMBr);
+		/* BB also check enough total bytes returned */
+		if (rc || get_bcc(&pSMBr->hdr) < 2)
+			/* If rc should we check for EOPNOSUPP and
+			disable the srvino flag? or in caller? */
+			rc = -EIO;      /* bad smb */
+		else {
+			__u16 data_offset = le16_to_cpu(pSMBr->t2.DataOffset);
+			__u16 count = le16_to_cpu(pSMBr->t2.DataCount);
+			struct file_internal_info *pfinfo;
+			/* BB Do we need a cast or hash here ? */
+			if (count < 8) {
+				cFYI(1, "Illegal size ret in FileQryIntrnlInf");
+				rc = -EIO;
+				goto FileGetInodeNumOut;
+			}
+			pfinfo = (struct file_internal_info *)
+				(data_offset + (char *) &pSMBr->hdr.Protocol);
+			*inode_number = le64_to_cpu(pfinfo->UniqueId);
+		}
+	}
+FileGetInodeNumOut:
+	cifs_buf_release(pSMB);
+	if (rc == -EAGAIN)
+		goto FileGetInodeNumberRetry;
+	return rc;
+}
+
 /* parses DFS refferal V3 structure
  * caller is responsible for freeing target_nodes
  * returns:
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 745da3d..a7c07a0 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -604,7 +604,8 @@  int cifs_get_inode_info(struct inode **pinode,
 	const unsigned char *full_path, FILE_ALL_INFO *pfindData,
 	struct super_block *sb, int xid, const __u16 *pfid)
 {
-	int rc = 0, tmprc;
+	__u16 fid;
+	int rc = 0, rc2 = -EINVAL, tmprc, oplock = 0;
 	struct cifs_tcon *pTcon;
 	struct tcon_link *tlink;
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -640,6 +641,23 @@  int cifs_get_inode_info(struct inode **pinode,
 			      0 /* not legacy */,
 			      cifs_sb->local_nls, cifs_sb->mnt_cifs_flags &
 				CIFS_MOUNT_MAP_SPECIAL_CHR);
+
+		/*
+		 * This sequence of calls is meant for a lookup operation
+		 * that would otherwise fail since query path info has no
+		 * means to indicate backup intent.
+		 */
+		if (rc == -EACCES && *pinode == NULL && backup_cred(cifs_sb)) {
+			/* pfindData contains same info returned by qpathinfo */
+			rc2 = CIFSSMBOpen(xid, pTcon, full_path,
+				FILE_OPEN, GENERIC_READ,
+				CREATE_OPEN_BACKUP_INTENT, &fid, &oplock,
+				pfindData, cifs_sb->local_nls,
+				cifs_sb->mnt_cifs_flags &
+					CIFS_MOUNT_MAP_SPECIAL_CHR);
+			rc = rc2;
+		}
+
 		/* 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 in tcon or mount */
@@ -683,7 +701,14 @@  int cifs_get_inode_info(struct inode **pinode,
 		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
 			int rc1 = 0;
 
-			rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
+			if (!rc2)
+				rc1 = CIFSGetFileSrvInodeNumber(xid, pTcon,
+					fid, &fattr.cf_uniqueid,
+					cifs_sb->local_nls,
+					cifs_sb->mnt_cifs_flags &
+						CIFS_MOUNT_MAP_SPECIAL_CHR);
+			else
+				rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
 					full_path, &fattr.cf_uniqueid,
 					cifs_sb->local_nls,
 					cifs_sb->mnt_cifs_flags &
@@ -699,6 +724,8 @@  int cifs_get_inode_info(struct inode **pinode,
 	} else {
 		fattr.cf_uniqueid = CIFS_I(*pinode)->uniqueid;
 	}
+	if (!rc2)
+		CIFSSMBClose(xid, pTcon, fid);
 
 	/* query for SFU type info if supported and needed */
 	if (fattr.cf_cifsattrs & ATTR_SYSTEM &&