Commit b9a3260f authored by Steve French's avatar Steve French

[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: default avatarSteve French <sfrench@us.ibm.com>
parent 0e4bbde9
...@@ -161,6 +161,12 @@ static void cifs_unix_info_to_inode(struct inode *inode, ...@@ -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, ...@@ -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, ...@@ -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: ...@@ -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; inode = *pinode;
cifsInfo = CIFS_I(inode); cifsInfo = CIFS_I(inode);
cifsInfo->cifsAttrs = attr; cifsInfo->cifsAttrs = attr;
cFYI(1, ("Old time %ld", cifsInfo->time)); cFYI(1, ("Old time %ld", cifsInfo->time));
cifsInfo->time = jiffies; cifsInfo->time = jiffies;
cFYI(1, ("New time %ld", cifsInfo->time)); cFYI(1, ("New time %ld", cifsInfo->time));
/* blksize needs to be multiple of two. So safer to default to /* blksize needs to be multiple of two. So safer to default to
blksize and blkbits set in superblock so 2**blkbits and blksize blksize and blkbits set in superblock so 2**blkbits and blksize
will match rather than setting to: will match rather than setting to:
(pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/ (pTcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE) & 0xFFFFFE00;*/
/* Linux can not store file creation time so ignore it */ /* Linux can not store file creation time so ignore it */
if (pfindData->LastAccessTime) if (pfindData->LastAccessTime)
inode->i_atime = cifs_NTtimeToUnix inode->i_atime = cifs_NTtimeToUnix
(le64_to_cpu(pfindData->LastAccessTime)); (le64_to_cpu(pfindData->LastAccessTime));
else /* do not need to use current_fs_time - time not stored */ else /* do not need to use current_fs_time - time not stored */
inode->i_atime = CURRENT_TIME; inode->i_atime = CURRENT_TIME;
inode->i_mtime = inode->i_mtime =
cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime)); cifs_NTtimeToUnix(le64_to_cpu(pfindData->LastWriteTime));
inode->i_ctime = inode->i_ctime =
cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime)); cifs_NTtimeToUnix(le64_to_cpu(pfindData->ChangeTime));
cFYI(0, ("Attributes came in as 0x%x", attr)); cFYI(DBG2, ("Attributes came in as 0x%x", attr));
if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) { if (adjustTZ && (pTcon->ses) && (pTcon->ses->server)) {
inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj; inode->i_ctime.tv_sec += pTcon->ses->server->timeAdj;
inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj; inode->i_mtime.tv_sec += pTcon->ses->server->timeAdj;
} }
/* set default mode. will override for dirs below */ /* set default mode. will override for dirs below */
if (atomic_read(&cifsInfo->inUse) == 0) if (atomic_read(&cifsInfo->inUse) == 0)
/* new inode, can safely set these fields */ /* new inode, can safely set these fields */
inode->i_mode = cifs_sb->mnt_file_mode; inode->i_mode = cifs_sb->mnt_file_mode;
else /* since we set the inode type below we need to mask off else /* since we set the inode type below we need to mask off
to avoid strange results if type changes and both to avoid strange results if type changes and both
get orred in */ get orred in */
inode->i_mode &= ~S_IFMT; inode->i_mode &= ~S_IFMT;
/* if (attr & ATTR_REPARSE) */ /* if (attr & ATTR_REPARSE) */
/* We no longer handle these as symlinks because we could not /* We no longer handle these as symlinks because we could not
follow them due to the absolute path with drive letter */ follow them due to the absolute path with drive letter */
if (attr & ATTR_DIRECTORY) { if (attr & ATTR_DIRECTORY) {
/* override default perms since we do not do byte range locking /* override default perms since we do not do byte range locking
on dirs */ on dirs */
inode->i_mode = cifs_sb->mnt_dir_mode; inode->i_mode = cifs_sb->mnt_dir_mode;
inode->i_mode |= S_IFDIR; inode->i_mode |= S_IFDIR;
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
(cifsInfo->cifsAttrs & ATTR_SYSTEM) && (cifsInfo->cifsAttrs & ATTR_SYSTEM) &&
/* No need to le64 convert size of zero */ /* No need to le64 convert size of zero */
(pfindData->EndOfFile == 0)) { (pfindData->EndOfFile == 0)) {
inode->i_mode = cifs_sb->mnt_file_mode; inode->i_mode = cifs_sb->mnt_file_mode;
inode->i_mode |= S_IFIFO; inode->i_mode |= S_IFIFO;
/* BB Finish for SFU style symlinks and devices */ /* BB Finish for SFU style symlinks and devices */
} else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) && } else if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) &&
(cifsInfo->cifsAttrs & ATTR_SYSTEM)) { (cifsInfo->cifsAttrs & ATTR_SYSTEM)) {
if (decode_sfu_inode(inode, if (decode_sfu_inode(inode, le64_to_cpu(pfindData->EndOfFile),
le64_to_cpu(pfindData->EndOfFile), full_path, cifs_sb, xid))
full_path, cFYI(1, ("Unrecognized sfu inode type"));
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); cFYI(1, ("sfu mode 0%o", inode->i_mode));
if (is_size_safe_to_change(cifsInfo, } else {
le64_to_cpu(pfindData->EndOfFile))) { inode->i_mode |= S_IFREG;
/* can not safely shrink the file size here if the /* treat dos attribute of read-only as read-only mode eg 555 */
client is writing to it due to potential races */ if (cifsInfo->cifsAttrs & ATTR_READONLY)
i_size_write(inode, le64_to_cpu(pfindData->EndOfFile)); inode->i_mode &= ~(S_IWUGO);
else if ((inode->i_mode & S_IWUGO) == 0)
/* 512 bytes (2**9) is the fake blocksize that must be /* the ATTR_READONLY flag may have been */
used for this calculation */ /* changed on server -- set any w bits */
inode->i_blocks = (512 - 1 + le64_to_cpu( /* allowed by mnt_file_mode */
pfindData->AllocationSize)) >> 9; inode->i_mode |= (S_IWUGO & cifs_sb->mnt_file_mode);
} /* BB add code to validate if device or weird share or device type? */
spin_unlock(&inode->i_lock); }
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); inode->i_nlink = le32_to_cpu(pfindData->NumberOfLinks);
/* BB fill in uid and gid here? with help from winbind? /* BB fill in uid and gid here? with help from winbind?
or retrieve from NTFS stream extended attribute */ or retrieve from NTFS stream extended attribute */
#ifdef CONFIG_CIFS_EXPERIMENTAL #ifdef CONFIG_CIFS_EXPERIMENTAL
/* fill in 0777 bits from ACL */ /* fill in 0777 bits from ACL */
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_CIFS_ACL) {
cFYI(1, ("Getting mode bits from ACL")); cFYI(1, ("Getting mode bits from ACL"));
acl_to_uid_mode(inode, full_path, pfid); acl_to_uid_mode(inode, full_path, pfid);
} }
#endif #endif
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) { if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UNX_EMUL) {
/* fill in remaining high mode bits e.g. SUID, VTX */ /* fill in remaining high mode bits e.g. SUID, VTX */
get_sfu_mode(inode, full_path, cifs_sb, xid); get_sfu_mode(inode, full_path, cifs_sb, xid);
} else if (atomic_read(&cifsInfo->inUse) == 0) { } else if (atomic_read(&cifsInfo->inUse) == 0) {
inode->i_uid = cifs_sb->mnt_uid; inode->i_uid = cifs_sb->mnt_uid;
inode->i_gid = cifs_sb->mnt_gid; inode->i_gid = cifs_sb->mnt_gid;
/* set so we do not keep refreshing these fields with /* set so we do not keep refreshing these fields with
bad data after user has changed them in memory */ bad data after user has changed them in memory */
atomic_set(&cifsInfo->inUse, 1); atomic_set(&cifsInfo->inUse, 1);
}
cifs_set_ops(inode, is_dfs_referral);
} }
cifs_set_ops(inode, is_dfs_referral);
cgii_exit: cgii_exit:
kfree(buf); kfree(buf);
return rc; return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment