Skip to content

Commit 6a5f659

Browse files
committed
SMB311: Add support for query info using posix extensions (level 100)
Adds support for better query info on dentry revalidation (using the SMB3.1.1 POSIX extensions level 100). Followon patch will add support for translating the UID/GID from the SID and also will add support for using the posix query info on lookup. Signed-off-by: Steve French <[email protected]> Reviewed-by: Ronnie Sahlberg <[email protected]> Reviewed-by: Aurelien Aptel <[email protected]>
1 parent ebf5744 commit 6a5f659

File tree

6 files changed

+316
-2
lines changed

6 files changed

+316
-2
lines changed

fs/cifs/cifsproto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ extern struct inode *cifs_iget(struct super_block *sb,
198198
extern int cifs_get_inode_info(struct inode **inode, const char *full_path,
199199
FILE_ALL_INFO *data, struct super_block *sb,
200200
int xid, const struct cifs_fid *fid);
201+
extern int smb311_posix_get_inode_info(struct inode **pinode, const char *search_path,
202+
struct super_block *sb, unsigned int xid);
201203
extern int cifs_get_inode_info_unix(struct inode **pinode,
202204
const unsigned char *search_path,
203205
struct super_block *sb, unsigned int xid);

fs/cifs/inode.c

Lines changed: 175 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include "cifspdu.h"
3333
#include "cifsglob.h"
3434
#include "cifsproto.h"
35+
#include "smb2proto.h"
3536
#include "cifs_debug.h"
3637
#include "cifs_fs_sb.h"
3738
#include "cifs_unicode.h"
@@ -595,6 +596,62 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
595596
#endif
596597
}
597598

599+
/* Fill a cifs_fattr struct with info from POSIX info struct */
600+
static void
601+
smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct smb311_posix_qinfo *info,
602+
struct super_block *sb, bool adjust_tz, bool symlink)
603+
{
604+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
605+
struct cifs_tcon *tcon = cifs_sb_master_tcon(cifs_sb);
606+
607+
memset(fattr, 0, sizeof(*fattr));
608+
609+
/* no fattr->flags to set */
610+
fattr->cf_cifsattrs = le32_to_cpu(info->DosAttributes);
611+
fattr->cf_uniqueid = le64_to_cpu(info->Inode);
612+
613+
if (info->LastAccessTime)
614+
fattr->cf_atime = cifs_NTtimeToUnix(info->LastAccessTime);
615+
else
616+
ktime_get_coarse_real_ts64(&fattr->cf_atime);
617+
618+
fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
619+
fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
620+
621+
if (adjust_tz) {
622+
fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
623+
fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
624+
}
625+
626+
fattr->cf_eof = le64_to_cpu(info->EndOfFile);
627+
fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
628+
fattr->cf_createtime = le64_to_cpu(info->CreationTime);
629+
630+
fattr->cf_nlink = le32_to_cpu(info->HardLinks);
631+
fattr->cf_mode = (umode_t) le32_to_cpu(info->Mode);
632+
/* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
633+
/* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
634+
635+
if (symlink) {
636+
fattr->cf_mode |= S_IFLNK;
637+
fattr->cf_dtype = DT_LNK;
638+
} else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
639+
fattr->cf_mode |= S_IFDIR;
640+
fattr->cf_dtype = DT_DIR;
641+
} else { /* file */
642+
fattr->cf_mode |= S_IFREG;
643+
fattr->cf_dtype = DT_REG;
644+
}
645+
/* else if reparse point ... TODO: add support for FIFO and blk dev; special file types */
646+
647+
fattr->cf_uid = cifs_sb->mnt_uid; /* TODO: map uid and gid from SID */
648+
fattr->cf_gid = cifs_sb->mnt_gid;
649+
650+
cifs_dbg(FYI, "POSIX query info: mode 0x%x uniqueid 0x%llx nlink %d\n",
651+
fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
652+
}
653+
654+
598655
/* Fill a cifs_fattr struct with info from FILE_ALL_INFO */
599656
static void
600657
cifs_all_info_to_fattr(struct cifs_fattr *fattr, FILE_ALL_INFO *info,
@@ -1023,6 +1080,121 @@ cifs_get_inode_info(struct inode **inode,
10231080
return rc;
10241081
}
10251082

1083+
int
1084+
smb311_posix_get_inode_info(struct inode **inode,
1085+
const char *full_path,
1086+
struct super_block *sb, unsigned int xid)
1087+
{
1088+
struct cifs_tcon *tcon;
1089+
struct TCP_Server_Info *server;
1090+
struct tcon_link *tlink;
1091+
struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
1092+
bool adjust_tz = false;
1093+
struct cifs_fattr fattr = {0};
1094+
bool symlink = false;
1095+
struct smb311_posix_qinfo *data = NULL;
1096+
int rc = 0;
1097+
int tmprc = 0;
1098+
1099+
tlink = cifs_sb_tlink(cifs_sb);
1100+
if (IS_ERR(tlink))
1101+
return PTR_ERR(tlink);
1102+
tcon = tlink_tcon(tlink);
1103+
server = tcon->ses->server;
1104+
1105+
/*
1106+
* 1. Fetch file metadata
1107+
*/
1108+
1109+
if (is_inode_cache_good(*inode)) {
1110+
cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
1111+
goto out;
1112+
}
1113+
data = kmalloc(sizeof(struct smb311_posix_qinfo), GFP_KERNEL);
1114+
if (!data) {
1115+
rc = -ENOMEM;
1116+
goto out;
1117+
}
1118+
1119+
rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
1120+
full_path, data,
1121+
&adjust_tz, &symlink);
1122+
1123+
/*
1124+
* 2. Convert it to internal cifs metadata (fattr)
1125+
*/
1126+
1127+
switch (rc) {
1128+
case 0:
1129+
smb311_posix_info_to_fattr(&fattr, data, sb, adjust_tz, symlink);
1130+
break;
1131+
case -EREMOTE:
1132+
/* DFS link, no metadata available on this server */
1133+
cifs_create_dfs_fattr(&fattr, sb);
1134+
rc = 0;
1135+
break;
1136+
case -EACCES:
1137+
/*
1138+
* For SMB2 and later the backup intent flag
1139+
* is already sent if needed on open and there
1140+
* is no path based FindFirst operation to use
1141+
* to retry with so nothing we can do, bail out
1142+
*/
1143+
goto out;
1144+
default:
1145+
cifs_dbg(FYI, "%s: unhandled err rc %d\n", __func__, rc);
1146+
goto out;
1147+
}
1148+
1149+
1150+
/*
1151+
* 4. Tweak fattr based on mount options
1152+
*/
1153+
1154+
/* check for Minshall+French symlinks */
1155+
if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MF_SYMLINKS) {
1156+
tmprc = check_mf_symlink(xid, tcon, cifs_sb, &fattr,
1157+
full_path);
1158+
if (tmprc)
1159+
cifs_dbg(FYI, "check_mf_symlink: %d\n", tmprc);
1160+
}
1161+
1162+
/*
1163+
* 5. Update inode with final fattr data
1164+
*/
1165+
1166+
if (!*inode) {
1167+
*inode = cifs_iget(sb, &fattr);
1168+
if (!*inode)
1169+
rc = -ENOMEM;
1170+
} else {
1171+
/* we already have inode, update it */
1172+
1173+
/* if uniqueid is different, return error */
1174+
if (unlikely(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_SERVER_INUM &&
1175+
CIFS_I(*inode)->uniqueid != fattr.cf_uniqueid)) {
1176+
CIFS_I(*inode)->time = 0; /* force reval */
1177+
rc = -ESTALE;
1178+
goto out;
1179+
}
1180+
1181+
/* if filetype is different, return error */
1182+
if (unlikely(((*inode)->i_mode & S_IFMT) !=
1183+
(fattr.cf_mode & S_IFMT))) {
1184+
CIFS_I(*inode)->time = 0; /* force reval */
1185+
rc = -ESTALE;
1186+
goto out;
1187+
}
1188+
1189+
cifs_fattr_to_inode(*inode, &fattr);
1190+
}
1191+
out:
1192+
cifs_put_tlink(tlink);
1193+
kfree(data);
1194+
return rc;
1195+
}
1196+
1197+
10261198
static const struct inode_operations cifs_ipc_inode_ops = {
10271199
.lookup = cifs_lookup,
10281200
};
@@ -2114,7 +2286,9 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
21142286
dentry, cifs_get_time(dentry), jiffies);
21152287

21162288
again:
2117-
if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
2289+
if (cifs_sb_master_tcon(CIFS_SB(sb))->posix_extensions)
2290+
rc = smb311_posix_get_inode_info(&inode, full_path, sb, xid);
2291+
else if (cifs_sb_master_tcon(CIFS_SB(sb))->unix_ext)
21182292
rc = cifs_get_inode_info_unix(&inode, full_path, sb, xid);
21192293
else
21202294
rc = cifs_get_inode_info(&inode, full_path, NULL, sb,

fs/cifs/smb2glob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#define SMB2_OP_HARDLINK 8
4646
#define SMB2_OP_SET_EOF 9
4747
#define SMB2_OP_RMDIR 10
48+
#define SMB2_OP_POSIX_QUERY_INFO 11
4849

4950
/* Used when constructing chained read requests. */
5051
#define CHAINED_REQUEST 1

fs/cifs/smb2inode.c

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,41 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
160160
}
161161
}
162162

163+
if (rc)
164+
goto finished;
165+
num_rqst++;
166+
trace_smb3_query_info_compound_enter(xid, ses->Suid, tcon->tid,
167+
full_path);
168+
break;
169+
case SMB2_OP_POSIX_QUERY_INFO:
170+
rqst[num_rqst].rq_iov = &vars->qi_iov[0];
171+
rqst[num_rqst].rq_nvec = 1;
172+
173+
if (cfile)
174+
rc = SMB2_query_info_init(tcon, server,
175+
&rqst[num_rqst],
176+
cfile->fid.persistent_fid,
177+
cfile->fid.volatile_fid,
178+
SMB_FIND_FILE_POSIX_INFO,
179+
SMB2_O_INFO_FILE, 0,
180+
/* TBD: fix following to allow for longer SIDs */
181+
sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
182+
(sizeof(struct cifs_sid) * 2), 0, NULL);
183+
else {
184+
rc = SMB2_query_info_init(tcon, server,
185+
&rqst[num_rqst],
186+
COMPOUND_FID,
187+
COMPOUND_FID,
188+
SMB_FIND_FILE_POSIX_INFO,
189+
SMB2_O_INFO_FILE, 0,
190+
sizeof(struct smb311_posix_qinfo *) + (PATH_MAX * 2) +
191+
(sizeof(struct cifs_sid) * 2), 0, NULL);
192+
if (!rc) {
193+
smb2_set_next_command(tcon, &rqst[num_rqst]);
194+
smb2_set_related(&rqst[num_rqst]);
195+
}
196+
}
197+
163198
if (rc)
164199
goto finished;
165200
num_rqst++;
@@ -379,6 +414,26 @@ smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
379414
trace_smb3_query_info_compound_done(xid, ses->Suid,
380415
tcon->tid);
381416
break;
417+
case SMB2_OP_POSIX_QUERY_INFO:
418+
if (rc == 0) {
419+
qi_rsp = (struct smb2_query_info_rsp *)
420+
rsp_iov[1].iov_base;
421+
rc = smb2_validate_and_copy_iov(
422+
le16_to_cpu(qi_rsp->OutputBufferOffset),
423+
le32_to_cpu(qi_rsp->OutputBufferLength),
424+
&rsp_iov[1], sizeof(struct smb311_posix_qinfo) /* add SIDs */, ptr);
425+
}
426+
if (rqst[1].rq_iov)
427+
SMB2_query_info_free(&rqst[1]);
428+
if (rqst[2].rq_iov)
429+
SMB2_close_free(&rqst[2]);
430+
if (rc)
431+
trace_smb3_query_info_compound_err(xid, ses->Suid,
432+
tcon->tid, rc);
433+
else
434+
trace_smb3_query_info_compound_done(xid, ses->Suid,
435+
tcon->tid);
436+
break;
382437
case SMB2_OP_DELETE:
383438
if (rc)
384439
trace_smb3_delete_err(xid, ses->Suid, tcon->tid, rc);
@@ -512,6 +567,59 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
512567
return rc;
513568
}
514569

570+
571+
int
572+
smb311_posix_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
573+
struct cifs_sb_info *cifs_sb, const char *full_path,
574+
struct smb311_posix_qinfo *data, bool *adjust_tz, bool *symlink)
575+
{
576+
int rc;
577+
__u32 create_options = 0;
578+
struct cifsFileInfo *cfile;
579+
struct smb311_posix_qinfo *smb2_data;
580+
581+
*adjust_tz = false;
582+
*symlink = false;
583+
584+
/* BB TODO: Make struct larger when add support for parsing owner SIDs */
585+
smb2_data = kzalloc(sizeof(struct smb311_posix_qinfo),
586+
GFP_KERNEL);
587+
if (smb2_data == NULL)
588+
return -ENOMEM;
589+
590+
/*
591+
* BB TODO: Add support for using the cached root handle.
592+
* Create SMB2_query_posix_info worker function to do non-compounded query
593+
* when we already have an open file handle for this. For now this is fast enough
594+
* (always using the compounded version).
595+
*/
596+
597+
cifs_get_readable_path(tcon, full_path, &cfile);
598+
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
599+
FILE_READ_ATTRIBUTES, FILE_OPEN, create_options,
600+
ACL_NO_MODE, smb2_data, SMB2_OP_POSIX_QUERY_INFO, cfile);
601+
if (rc == -EOPNOTSUPP) {
602+
/* BB TODO: When support for special files added to Samba re-verify this path */
603+
*symlink = true;
604+
create_options |= OPEN_REPARSE_POINT;
605+
606+
/* Failed on a symbolic link - query a reparse point info */
607+
rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
608+
FILE_READ_ATTRIBUTES, FILE_OPEN,
609+
create_options, ACL_NO_MODE,
610+
smb2_data, SMB2_OP_POSIX_QUERY_INFO, NULL);
611+
}
612+
if (rc)
613+
goto out;
614+
615+
/* TODO: will need to allow for the 2 SIDs when add support for getting owner UID/GID */
616+
memcpy(data, smb2_data, sizeof(struct smb311_posix_qinfo));
617+
618+
out:
619+
kfree(smb2_data);
620+
return rc;
621+
}
622+
515623
int
516624
smb2_mkdir(const unsigned int xid, struct inode *parent_inode, umode_t mode,
517625
struct cifs_tcon *tcon, const char *name,

fs/cifs/smb2pdu.h

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1653,7 +1653,7 @@ struct create_posix_rsp {
16531653
} __packed;
16541654

16551655
/*
1656-
* SMB2-only POSIX info level
1656+
* SMB2-only POSIX info level for query dir
16571657
*
16581658
* See posix_info_sid_size(), posix_info_extra_size() and
16591659
* posix_info_parse() to help with the handling of this struct.
@@ -1683,6 +1683,31 @@ struct smb2_posix_info {
16831683
*/
16841684
} __packed;
16851685

1686+
/* Level 100 query info */
1687+
struct smb311_posix_qinfo {
1688+
__le64 CreationTime;
1689+
__le64 LastAccessTime;
1690+
__le64 LastWriteTime;
1691+
__le64 ChangeTime;
1692+
__le64 EndOfFile;
1693+
__le64 AllocationSize;
1694+
__le32 DosAttributes;
1695+
__le64 Inode;
1696+
__le32 DeviceId;
1697+
__le32 Zero;
1698+
/* beginning of POSIX Create Context Response */
1699+
__le32 HardLinks;
1700+
__le32 ReparseTag;
1701+
__le32 Mode;
1702+
u8 Sids[];
1703+
/*
1704+
* var sized owner SID
1705+
* var sized group SID
1706+
* le32 filenamelength
1707+
* u8 filename[]
1708+
*/
1709+
} __packed;
1710+
16861711
/*
16871712
* Parsed version of the above struct. Allows direct access to the
16881713
* variable length fields

0 commit comments

Comments
 (0)