[CIFS] Enable DFS support for Windows query path info
Final piece for handling DFS in query_path_info, constructing a fake inode for the junction directory which the submount will cover. This handles the non-Unix (Windows etc.) code path. Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
340
fs/cifs/inode.c
340
fs/cifs/inode.c
@@ -161,6 +161,12 @@ static void cifs_unix_info_to_inode(struct inode *inode,
|
|||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Needed to setup inode data for the directory which is the
|
||||||
|
* junction to the new submount (ie to setup the fake directory
|
||||||
|
* which represents a DFS referral)
|
||||||
|
*/
|
||||||
static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
|
static void fill_fake_finddataunix(FILE_UNIX_BASIC_INFO *pfnd_dat,
|
||||||
struct super_block *sb)
|
struct super_block *sb)
|
||||||
{
|
{
|
||||||
@@ -370,11 +376,42 @@ static int get_sfu_mode(struct inode *inode,
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Needed to setup inode data for the directory which is the
|
||||||
|
* junction to the new submount (ie to setup the fake directory
|
||||||
|
* which represents a DFS referral)
|
||||||
|
*/
|
||||||
|
static void fill_fake_finddata(FILE_ALL_INFO *pfnd_dat,
|
||||||
|
struct super_block *sb)
|
||||||
|
{
|
||||||
|
memset(pfnd_dat, sizeof(FILE_ALL_INFO), 0);
|
||||||
|
|
||||||
|
/* __le64 pfnd_dat->AllocationSize = cpu_to_le64(0);
|
||||||
|
__le64 pfnd_dat->EndOfFile = cpu_to_le64(0);
|
||||||
|
__u8 pfnd_dat->DeletePending = 0;
|
||||||
|
__u8 pfnd_data->Directory = 0;
|
||||||
|
__le32 pfnd_dat->EASize = 0;
|
||||||
|
__u64 pfnd_dat->IndexNumber = 0;
|
||||||
|
__u64 pfnd_dat->IndexNumber1 = 0; */
|
||||||
|
pfnd_dat->CreationTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
|
||||||
|
pfnd_dat->LastAccessTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
|
||||||
|
pfnd_dat->LastWriteTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
|
||||||
|
pfnd_dat->ChangeTime =
|
||||||
|
cpu_to_le64(cifs_UnixTimeToNT(CURRENT_TIME));
|
||||||
|
pfnd_dat->Attributes = cpu_to_le32(ATTR_DIRECTORY);
|
||||||
|
pfnd_dat->NumberOfLinks = cpu_to_le32(2);
|
||||||
|
}
|
||||||
|
|
||||||
int cifs_get_inode_info(struct inode **pinode,
|
int cifs_get_inode_info(struct inode **pinode,
|
||||||
const unsigned char *full_path, FILE_ALL_INFO *pfindData,
|
const unsigned char *full_path, FILE_ALL_INFO *pfindData,
|
||||||
struct super_block *sb, int xid, const __u16 *pfid)
|
struct super_block *sb, int xid, const __u16 *pfid)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
__u32 attr;
|
||||||
|
struct cifsInodeInfo *cifsInfo;
|
||||||
struct cifsTconInfo *pTcon;
|
struct cifsTconInfo *pTcon;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
|
||||||
@@ -399,7 +436,6 @@ int cifs_get_inode_info(struct inode **pinode,
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
pfindData = (FILE_ALL_INFO *)buf;
|
pfindData = (FILE_ALL_INFO *)buf;
|
||||||
|
|
||||||
try_again_CIFSSMBQPathInfo:
|
|
||||||
/* could do find first instead but this returns more info */
|
/* could do find first instead but this returns more info */
|
||||||
rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData,
|
rc = CIFSSMBQPathInfo(xid, pTcon, full_path, pfindData,
|
||||||
0 /* not legacy */,
|
0 /* not legacy */,
|
||||||
@@ -417,171 +453,167 @@ try_again_CIFSSMBQPathInfo:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
|
/* dump_mem("\nQPathInfo return data",&findData, sizeof(findData)); */
|
||||||
if (rc) {
|
if (rc == -EREMOTE) {
|
||||||
if (rc == -EREMOTE && !is_dfs_referral) {
|
is_dfs_referral = true;
|
||||||
is_dfs_referral = true;
|
fill_fake_finddata(pfindData, sb);
|
||||||
goto try_again_CIFSSMBQPathInfo;
|
rc = 0;
|
||||||
}
|
} else if (rc)
|
||||||
goto cgii_exit;
|
goto cgii_exit;
|
||||||
} else {
|
|
||||||
struct cifsInodeInfo *cifsInfo;
|
|
||||||
__u32 attr = le32_to_cpu(pfindData->Attributes);
|
|
||||||
|
|
||||||
/* get new inode */
|
attr = le32_to_cpu(pfindData->Attributes);
|
||||||
|
|
||||||
|
/* get new inode */
|
||||||
|
if (*pinode == NULL) {
|
||||||
|
*pinode = new_inode(sb);
|
||||||
if (*pinode == NULL) {
|
if (*pinode == NULL) {
|
||||||
*pinode = new_inode(sb);
|
rc = -ENOMEM;
|
||||||
if (*pinode == NULL) {
|
goto cgii_exit;
|
||||||
rc = -ENOMEM;
|
}
|
||||||
goto cgii_exit;
|
/* Is an i_ino of zero legal? Can we use that to check
|
||||||
}
|
if the server supports returning inode numbers? Are
|
||||||
/* Is an i_ino of zero legal? Can we use that to check
|
there other sanity checks we can use to ensure that
|
||||||
if the server supports returning inode numbers? Are
|
the server is really filling in that field? */
|
||||||
there other sanity checks we can use to ensure that
|
|
||||||
the server is really filling in that field? */
|
|
||||||
|
|
||||||
/* We can not use the IndexNumber field by default from
|
/* We can not use the IndexNumber field by default from
|
||||||
Windows or Samba (in ALL_INFO buf) but we can request
|
Windows or Samba (in ALL_INFO buf) but we can request
|
||||||
it explicitly. It may not be unique presumably if
|
it explicitly. It may not be unique presumably if
|
||||||
the server has multiple devices mounted under one
|
the server has multiple devices mounted under one share */
|
||||||
share */
|
|
||||||
|
|
||||||
/* There may be higher info levels that work but are
|
/* There may be higher info levels that work but are
|
||||||
there Windows server or network appliances for which
|
there Windows server or network appliances for which
|
||||||
IndexNumber field is not guaranteed unique? */
|
IndexNumber field is not guaranteed unique? */
|
||||||
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM) {
|
||||||
int rc1 = 0;
|
int rc1 = 0;
|
||||||
__u64 inode_num;
|
__u64 inode_num;
|
||||||
|
|
||||||
rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
|
rc1 = CIFSGetSrvInodeNumber(xid, pTcon,
|
||||||
full_path, &inode_num,
|
full_path, &inode_num,
|
||||||
cifs_sb->local_nls,
|
cifs_sb->local_nls,
|
||||||
cifs_sb->mnt_cifs_flags &
|
cifs_sb->mnt_cifs_flags &
|
||||||
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
CIFS_MOUNT_MAP_SPECIAL_CHR);
|
||||||
if (rc1) {
|
if (rc1) {
|
||||||
cFYI(1, ("GetSrvInodeNum rc %d", rc1));
|
cFYI(1, ("GetSrvInodeNum rc %d", rc1));
|
||||||
/* BB EOPNOSUPP disable SERVER_INUM? */
|
/* BB EOPNOSUPP disable SERVER_INUM? */
|
||||||
} else /* do we need cast or hash to ino? */
|
} else /* do we need cast or hash to ino? */
|
||||||
(*pinode)->i_ino = inode_num;
|
(*pinode)->i_ino = inode_num;
|
||||||
} /* else ino incremented to unique num in new_inode*/
|
} /* else ino incremented to unique num in new_inode*/
|
||||||
if (sb->s_flags & MS_NOATIME)
|
if (sb->s_flags & MS_NOATIME)
|
||||||
(*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
|
(*pinode)->i_flags |= S_NOATIME | S_NOCMTIME;
|
||||||
insert_inode_hash(*pinode);
|
insert_inode_hash(*pinode);
|
||||||
}
|
|
||||||
inode = *pinode;
|
|
||||||
cifsInfo = CIFS_I(inode);
|
|
||||||
cifsInfo->cifsAttrs = attr;
|
|
||||||
cFYI(1, ("Old time %ld", cifsInfo->time));
|
|
||||||
cifsInfo->time = jiffies;
|
|
||||||
cFYI(1, ("New time %ld", cifsInfo->time));
|
|
||||||
|
|
||||||
/* blksize needs to be multiple of two. So safer to default to
|
|
||||||
blksize and blkbits set in superblock so 2**blkbits and blksize
|
|
||||||
will match rather than setting to:
|
|
||||||
(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
|
|
||||||
|
|
||||||
/* Linux can not store file creation time so ignore it */
|
|
||||||
if (pfindData->LastAccessTime)
|
|
||||||
inode->i_atime = cifs_NTtimeToUnix
|
|
||||||
(le64_to_cpu(pfindData->LastAccessTime));
|
|
||||||
else /* do not need to use current_fs_time - time not stored */
|
|
||||||
inode->i_atime = CURRENT_TIME;
|
|
||||||
inode->i_mtime =
|
|
||||||
cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
|
|
||||||
inode->i_ctime =
|
|
||||||
cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
|
|
||||||
cFYI(0, ("Attributes came in as 0x%x", attr));
|
|
||||||
if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
|
|
||||||
inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
|
|
||||||
inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* set default mode. will override for dirs below */
|
|
||||||
if (atomic_read(&cifsInfo->inUse) == 0)
|
|
||||||
/* new inode, can safely set these fields */
|
|
||||||
inode->i_mode = cifs_sb->mnt_file_mode;
|
|
||||||
else /* since we set the inode type below we need to mask off
|
|
||||||
to avoid strange results if type changes and both
|
|
||||||
get orred in */
|
|
||||||
inode->i_mode &= ~S_IFMT;
|
|
||||||
/* if (attr & ATTR_REPARSE) */
|
|
||||||
/* We no longer handle these as symlinks because we could not
|
|
||||||
follow them due to the absolute path with drive letter */
|
|
||||||
if (attr & ATTR_DIRECTORY) {
|
|
||||||
/* override default perms since we do not do byte range locking
|
|
||||||
on dirs */
|
|
||||||
inode->i_mode = cifs_sb->mnt_dir_mode;
|
|
||||||
inode->i_mode |= S_IFDIR;
|
|
||||||
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
|
|
||||||
(cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
|
|
||||||
/* No need to le64 convert size of zero */
|
|
||||||
(pfindData->EndOfFile == 0)) {
|
|
||||||
inode->i_mode = cifs_sb->mnt_file_mode;
|
|
||||||
inode->i_mode |= S_IFIFO;
|
|
||||||
/* BB Finish for SFU style symlinks and devices */
|
|
||||||
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
|
|
||||||
(cifsInfo->cifsAttrs & ATTR_SYSTEM)) {
|
|
||||||
if (decode_sfu_inode(inode,
|
|
||||||
le64_to_cpu(pfindData->EndOfFile),
|
|
||||||
full_path,
|
|
||||||
cifs_sb, xid))
|
|
||||||
cFYI(1, ("Unrecognized sfu inode type"));
|
|
||||||
|
|
||||||
cFYI(1, ("sfu mode 0%o", inode->i_mode));
|
|
||||||
} else {
|
|
||||||
inode->i_mode |= S_IFREG;
|
|
||||||
/* treat the dos attribute of read-only as read-only
|
|
||||||
mode e.g. 555 */
|
|
||||||
if (cifsInfo->cifsAttrs & ATTR_READONLY)
|
|
||||||
inode->i_mode &= ~(S_IWUGO);
|
|
||||||
else if ((inode->i_mode & S_IWUGO) == 0)
|
|
||||||
/* the ATTR_READONLY flag may have been */
|
|
||||||
/* changed on server -- set any w bits */
|
|
||||||
/* allowed by mnt_file_mode */
|
|
||||||
inode->i_mode |= (S_IWUGO &
|
|
||||||
cifs_sb->mnt_file_mode);
|
|
||||||
/* BB add code here -
|
|
||||||
validate if device or weird share or device type? */
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&inode->i_lock);
|
|
||||||
if (is_size_safe_to_change(cifsInfo,
|
|
||||||
le64_to_cpu(pfindData->EndOfFile))) {
|
|
||||||
/* can not safely shrink the file size here if the
|
|
||||||
client is writing to it due to potential races */
|
|
||||||
i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
|
|
||||||
|
|
||||||
/* 512 bytes (2**9) is the fake blocksize that must be
|
|
||||||
used for this calculation */
|
|
||||||
inode->i_blocks = (512 - 1 + le64_to_cpu(
|
|
||||||
pfindData->AllocationSize)) >> 9;
|
|
||||||
}
|
|
||||||
spin_unlock(&inode->i_lock);
|
|
||||||
|
|
||||||
inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
|
|
||||||
|
|
||||||
/* BB fill in uid and gid here? with help from winbind?
|
|
||||||
or retrieve from NTFS stream extended attribute */
|
|
||||||
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
|
||||||
/* fill in 0777 bits from ACL */
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
|
||||||
cFYI(1, ("Getting mode bits from ACL"));
|
|
||||||
acl_to_uid_mode(inode, full_path, pfid);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
|
||||||
/* fill in remaining high mode bits e.g. SUID, VTX */
|
|
||||||
get_sfu_mode(inode, full_path, cifs_sb, xid);
|
|
||||||
} else if (atomic_read(&cifsInfo->inUse) == 0) {
|
|
||||||
inode->i_uid = cifs_sb->mnt_uid;
|
|
||||||
inode->i_gid = cifs_sb->mnt_gid;
|
|
||||||
/* set so we do not keep refreshing these fields with
|
|
||||||
bad data after user has changed them in memory */
|
|
||||||
atomic_set(&cifsInfo->inUse, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
cifs_set_ops(inode, is_dfs_referral);
|
|
||||||
}
|
}
|
||||||
|
inode = *pinode;
|
||||||
|
cifsInfo = CIFS_I(inode);
|
||||||
|
cifsInfo->cifsAttrs = attr;
|
||||||
|
cFYI(1, ("Old time %ld", cifsInfo->time));
|
||||||
|
cifsInfo->time = jiffies;
|
||||||
|
cFYI(1, ("New time %ld", cifsInfo->time));
|
||||||
|
|
||||||
|
/* blksize needs to be multiple of two. So safer to default to
|
||||||
|
blksize and blkbits set in superblock so 2**blkbits and blksize
|
||||||
|
will match rather than setting to:
|
||||||
|
(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
|
||||||
|
|
||||||
|
/* Linux can not store file creation time so ignore it */
|
||||||
|
if (pfindData->LastAccessTime)
|
||||||
|
inode->i_atime = cifs_NTtimeToUnix
|
||||||
|
(le64_to_cpu(pfindData->LastAccessTime));
|
||||||
|
else /* do not need to use current_fs_time - time not stored */
|
||||||
|
inode->i_atime = CURRENT_TIME;
|
||||||
|
inode->i_mtime =
|
||||||
|
cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
|
||||||
|
inode->i_ctime =
|
||||||
|
cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
|
||||||
|
cFYI(DBG2, ("Attributes came in as 0x%x", attr));
|
||||||
|
if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
|
||||||
|
inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
|
||||||
|
inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set default mode. will override for dirs below */
|
||||||
|
if (atomic_read(&cifsInfo->inUse) == 0)
|
||||||
|
/* new inode, can safely set these fields */
|
||||||
|
inode->i_mode = cifs_sb->mnt_file_mode;
|
||||||
|
else /* since we set the inode type below we need to mask off
|
||||||
|
to avoid strange results if type changes and both
|
||||||
|
get orred in */
|
||||||
|
inode->i_mode &= ~S_IFMT;
|
||||||
|
/* if (attr & ATTR_REPARSE) */
|
||||||
|
/* We no longer handle these as symlinks because we could not
|
||||||
|
follow them due to the absolute path with drive letter */
|
||||||
|
if (attr & ATTR_DIRECTORY) {
|
||||||
|
/* override default perms since we do not do byte range locking
|
||||||
|
on dirs */
|
||||||
|
inode->i_mode = cifs_sb->mnt_dir_mode;
|
||||||
|
inode->i_mode |= S_IFDIR;
|
||||||
|
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
|
||||||
|
(cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
|
||||||
|
/* No need to le64 convert size of zero */
|
||||||
|
(pfindData->EndOfFile == 0)) {
|
||||||
|
inode->i_mode = cifs_sb->mnt_file_mode;
|
||||||
|
inode->i_mode |= S_IFIFO;
|
||||||
|
/* BB Finish for SFU style symlinks and devices */
|
||||||
|
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
|
||||||
|
(cifsInfo->cifsAttrs & ATTR_SYSTEM)) {
|
||||||
|
if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile),
|
||||||
|
full_path, cifs_sb, xid))
|
||||||
|
cFYI(1, ("Unrecognized sfu inode type"));
|
||||||
|
|
||||||
|
cFYI(1, ("sfu mode 0%o", inode->i_mode));
|
||||||
|
} else {
|
||||||
|
inode->i_mode |= S_IFREG;
|
||||||
|
/* treat dos attribute of read-only as read-only mode eg 555 */
|
||||||
|
if (cifsInfo->cifsAttrs & ATTR_READONLY)
|
||||||
|
inode->i_mode &= ~(S_IWUGO);
|
||||||
|
else if ((inode->i_mode & S_IWUGO) == 0)
|
||||||
|
/* the ATTR_READONLY flag may have been */
|
||||||
|
/* changed on server -- set any w bits */
|
||||||
|
/* allowed by mnt_file_mode */
|
||||||
|
inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode);
|
||||||
|
/* BB add code to validate if device or weird share or device type? */
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
|
if (is_size_safe_to_change(cifsInfo,
|
||||||
|
le64_to_cpu(pfindData->EndOfFile))) {
|
||||||
|
/* can not safely shrink the file size here if the
|
||||||
|
client is writing to it due to potential races */
|
||||||
|
i_size_write(inode, le64_to_cpu(pfindData->EndOfFile));
|
||||||
|
|
||||||
|
/* 512 bytes (2**9) is the fake blocksize that must be
|
||||||
|
used for this calculation */
|
||||||
|
inode->i_blocks = (512 - 1 + le64_to_cpu(
|
||||||
|
pfindData->AllocationSize)) >> 9;
|
||||||
|
}
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
|
||||||
|
inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
|
||||||
|
|
||||||
|
/* BB fill in uid and gid here? with help from winbind?
|
||||||
|
or retrieve from NTFS stream extended attribute */
|
||||||
|
#ifdef CONFIG_CIFS_EXPERIMENTAL
|
||||||
|
/* fill in 0777 bits from ACL */
|
||||||
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
|
||||||
|
cFYI(1, ("Getting mode bits from ACL"));
|
||||||
|
acl_to_uid_mode(inode, full_path, pfid);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
|
||||||
|
/* fill in remaining high mode bits e.g. SUID, VTX */
|
||||||
|
get_sfu_mode(inode, full_path, cifs_sb, xid);
|
||||||
|
} else if (atomic_read(&cifsInfo->inUse) == 0) {
|
||||||
|
inode->i_uid = cifs_sb->mnt_uid;
|
||||||
|
inode->i_gid = cifs_sb->mnt_gid;
|
||||||
|
/* set so we do not keep refreshing these fields with
|
||||||
|
bad data after user has changed them in memory */
|
||||||
|
atomic_set(&cifsInfo->inUse, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
cifs_set_ops(inode, is_dfs_referral);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
cgii_exit:
|
cgii_exit:
|
||||||
kfree(buf);
|
kfree(buf);
|
||||||
return rc;
|
return rc;
|
||||||
|
Reference in New Issue
Block a user