smb: client: do not query reparse points twice on symlinks
[platform/kernel/linux-starfive.git] / fs / smb / client / inode.c
index c3eeae0..96a0981 100644 (file)
@@ -58,13 +58,9 @@ static void cifs_set_ops(struct inode *inode)
                        inode->i_data.a_ops = &cifs_addr_ops;
                break;
        case S_IFDIR:
-#ifdef CONFIG_CIFS_DFS_UPCALL
                if (IS_AUTOMOUNT(inode)) {
-                       inode->i_op = &cifs_dfs_referral_inode_operations;
+                       inode->i_op = &cifs_namespace_inode_operations;
                } else {
-#else /* NO DFS support, treat as a directory */
-               {
-#endif
                        inode->i_op = &cifs_dir_inode_ops;
                        inode->i_fop = &cifs_dir_ops;
                }
@@ -432,7 +428,7 @@ int cifs_get_inode_info_unix(struct inode **pinode,
                if (!server->ops->query_symlink)
                        return -EOPNOTSUPP;
                rc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
-                                               &fattr.cf_symlink_target, false);
+                                               &fattr.cf_symlink_target, NULL);
                if (rc) {
                        cifs_dbg(FYI, "%s: query_symlink: %d\n", __func__, rc);
                        goto cgiiu_exit;
@@ -632,10 +628,11 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
 }
 
 /* Fill a cifs_fattr struct with info from POSIX info struct */
-static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
+static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr,
+                                      struct cifs_open_info_data *data,
                                       struct cifs_sid *owner,
                                       struct cifs_sid *group,
-                                      struct super_block *sb, bool adjust_tz, bool symlink)
+                                      struct super_block *sb)
 {
        struct smb311_posix_qinfo *info = &data->posix_fi;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -655,7 +652,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
        fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
        fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
 
-       if (adjust_tz) {
+       if (data->adjust_tz) {
                fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
                fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
        }
@@ -669,7 +666,7 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
        /* The srv fs device id is overridden on network mount so setting rdev isn't needed here */
        /* fattr->cf_rdev = le32_to_cpu(info->DeviceId); */
 
-       if (symlink) {
+       if (data->symlink) {
                fattr->cf_mode |= S_IFLNK;
                fattr->cf_dtype = DT_LNK;
                fattr->cf_symlink_target = data->symlink_target;
@@ -690,9 +687,46 @@ static void smb311_posix_info_to_fattr(struct cifs_fattr *fattr, struct cifs_ope
                fattr->cf_mode, fattr->cf_uniqueid, fattr->cf_nlink);
 }
 
-static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_info_data *data,
-                                   struct super_block *sb, bool adjust_tz, bool symlink,
-                                   u32 reparse_tag)
+bool cifs_reparse_point_to_fattr(struct cifs_sb_info *cifs_sb,
+                                struct cifs_fattr *fattr,
+                                u32 tag)
+{
+       switch (tag) {
+       case IO_REPARSE_TAG_LX_SYMLINK:
+               fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
+               fattr->cf_dtype = DT_LNK;
+               break;
+       case IO_REPARSE_TAG_LX_FIFO:
+               fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
+               fattr->cf_dtype = DT_FIFO;
+               break;
+       case IO_REPARSE_TAG_AF_UNIX:
+               fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
+               fattr->cf_dtype = DT_SOCK;
+               break;
+       case IO_REPARSE_TAG_LX_CHR:
+               fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
+               fattr->cf_dtype = DT_CHR;
+               break;
+       case IO_REPARSE_TAG_LX_BLK:
+               fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
+               fattr->cf_dtype = DT_BLK;
+               break;
+       case 0: /* SMB1 symlink */
+       case IO_REPARSE_TAG_SYMLINK:
+       case IO_REPARSE_TAG_NFS:
+               fattr->cf_mode = S_IFLNK;
+               fattr->cf_dtype = DT_LNK;
+               break;
+       default:
+               return false;
+       }
+       return true;
+}
+
+static void cifs_open_info_to_fattr(struct cifs_fattr *fattr,
+                                   struct cifs_open_info_data *data,
+                                   struct super_block *sb)
 {
        struct smb2_file_all_info *info = &data->fi;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
@@ -711,7 +745,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
        fattr->cf_ctime = cifs_NTtimeToUnix(info->ChangeTime);
        fattr->cf_mtime = cifs_NTtimeToUnix(info->LastWriteTime);
 
-       if (adjust_tz) {
+       if (data->adjust_tz) {
                fattr->cf_ctime.tv_sec += tcon->ses->server->timeAdj;
                fattr->cf_mtime.tv_sec += tcon->ses->server->timeAdj;
        }
@@ -719,28 +753,13 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
        fattr->cf_eof = le64_to_cpu(info->EndOfFile);
        fattr->cf_bytes = le64_to_cpu(info->AllocationSize);
        fattr->cf_createtime = le64_to_cpu(info->CreationTime);
-
        fattr->cf_nlink = le32_to_cpu(info->NumberOfLinks);
-       if (reparse_tag == IO_REPARSE_TAG_LX_SYMLINK) {
-               fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
-               fattr->cf_dtype = DT_LNK;
-       } else if (reparse_tag == IO_REPARSE_TAG_LX_FIFO) {
-               fattr->cf_mode |= S_IFIFO | cifs_sb->ctx->file_mode;
-               fattr->cf_dtype = DT_FIFO;
-       } else if (reparse_tag == IO_REPARSE_TAG_AF_UNIX) {
-               fattr->cf_mode |= S_IFSOCK | cifs_sb->ctx->file_mode;
-               fattr->cf_dtype = DT_SOCK;
-       } else if (reparse_tag == IO_REPARSE_TAG_LX_CHR) {
-               fattr->cf_mode |= S_IFCHR | cifs_sb->ctx->file_mode;
-               fattr->cf_dtype = DT_CHR;
-       } else if (reparse_tag == IO_REPARSE_TAG_LX_BLK) {
-               fattr->cf_mode |= S_IFBLK | cifs_sb->ctx->file_mode;
-               fattr->cf_dtype = DT_BLK;
-       } else if (symlink || reparse_tag == IO_REPARSE_TAG_SYMLINK ||
-                  reparse_tag == IO_REPARSE_TAG_NFS) {
-               fattr->cf_mode = S_IFLNK;
-               fattr->cf_dtype = DT_LNK;
-       } else if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
+
+       if (cifs_open_data_reparse(data) &&
+           cifs_reparse_point_to_fattr(cifs_sb, fattr, data->reparse_tag))
+               goto out_reparse;
+
+       if (fattr->cf_cifsattrs & ATTR_DIRECTORY) {
                fattr->cf_mode = S_IFDIR | cifs_sb->ctx->dir_mode;
                fattr->cf_dtype = DT_DIR;
                /*
@@ -769,6 +788,7 @@ static void cifs_open_info_to_fattr(struct cifs_fattr *fattr, struct cifs_open_i
                }
        }
 
+out_reparse:
        if (S_ISLNK(fattr->cf_mode)) {
                fattr->cf_symlink_target = data->symlink_target;
                data->symlink_target = NULL;
@@ -789,8 +809,6 @@ cifs_get_file_info(struct file *filp)
        struct cifsFileInfo *cfile = filp->private_data;
        struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
        struct TCP_Server_Info *server = tcon->ses->server;
-       bool symlink = false;
-       u32 tag = 0;
 
        if (!server->ops->query_file_info)
                return -ENOSYS;
@@ -800,11 +818,12 @@ cifs_get_file_info(struct file *filp)
        switch (rc) {
        case 0:
                /* TODO: add support to query reparse tag */
+               data.adjust_tz = false;
                if (data.symlink_target) {
-                       symlink = true;
-                       tag = IO_REPARSE_TAG_SYMLINK;
+                       data.symlink = true;
+                       data.reparse_tag = IO_REPARSE_TAG_SYMLINK;
                }
-               cifs_open_info_to_fattr(&fattr, &data, inode->i_sb, false, symlink, tag);
+               cifs_open_info_to_fattr(&fattr, &data, inode->i_sb);
                break;
        case -EREMOTE:
                cifs_create_dfs_fattr(&fattr, inode->i_sb);
@@ -960,6 +979,45 @@ static inline bool is_inode_cache_good(struct inode *ino)
        return ino && CIFS_CACHE_READ(CIFS_I(ino)) && CIFS_I(ino)->time != 0;
 }
 
+static int query_reparse(struct cifs_open_info_data *data,
+                        struct super_block *sb,
+                        const unsigned int xid,
+                        struct cifs_tcon *tcon,
+                        const char *full_path,
+                        struct cifs_fattr *fattr)
+{
+       struct TCP_Server_Info *server = tcon->ses->server;
+       struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
+       struct kvec rsp_iov, *iov = NULL;
+       int rsp_buftype = CIFS_NO_BUFFER;
+       u32 tag = data->reparse_tag;
+       int rc = 0;
+
+       if (!tag && server->ops->query_reparse_point) {
+               rc = server->ops->query_reparse_point(xid, tcon, cifs_sb,
+                                                     full_path, &tag,
+                                                     &rsp_iov, &rsp_buftype);
+               if (!rc)
+                       iov = &rsp_iov;
+       }
+       switch ((data->reparse_tag = tag)) {
+       case 0: /* SMB1 symlink */
+               iov = NULL;
+               fallthrough;
+       case IO_REPARSE_TAG_NFS:
+       case IO_REPARSE_TAG_SYMLINK:
+               if (!data->symlink_target && server->ops->query_symlink) {
+                       rc = server->ops->query_symlink(xid, tcon,
+                                                       cifs_sb, full_path,
+                                                       &data->symlink_target,
+                                                       iov);
+               }
+               break;
+       }
+       free_rsp_buf(rsp_buftype, rsp_iov.iov_base);
+       return rc;
+}
+
 int cifs_get_inode_info(struct inode **inode, const char *full_path,
                        struct cifs_open_info_data *data, struct super_block *sb, int xid,
                        const struct cifs_fid *fid)
@@ -968,14 +1026,11 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
        struct TCP_Server_Info *server;
        struct tcon_link *tlink;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-       bool adjust_tz = false;
        struct cifs_fattr fattr = {0};
-       bool is_reparse_point = false;
        struct cifs_open_info_data tmp_data = {};
        void *smb1_backup_rsp_buf = NULL;
        int rc = 0;
        int tmprc = 0;
-       __u32 reparse_tag = 0;
 
        tlink = cifs_sb_tlink(cifs_sb);
        if (IS_ERR(tlink))
@@ -992,8 +1047,8 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
                        cifs_dbg(FYI, "No need to revalidate cached inode sizes\n");
                        goto out;
                }
-               rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data,
-                                                 &adjust_tz, &is_reparse_point);
+               rc = server->ops->query_path_info(xid, tcon, cifs_sb,
+                                                 full_path, &tmp_data);
                data = &tmp_data;
        }
 
@@ -1008,24 +1063,12 @@ int cifs_get_inode_info(struct inode **inode, const char *full_path,
                 * since we have to check if its reparse tag matches a known
                 * special file type e.g. symlink or fifo or char etc.
                 */
-               if (is_reparse_point && data->symlink_target) {
-                       reparse_tag = IO_REPARSE_TAG_SYMLINK;
-               } else if ((le32_to_cpu(data->fi.Attributes) & ATTR_REPARSE) &&
-                          server->ops->query_reparse_tag) {
-                       tmprc = server->ops->query_reparse_tag(xid, tcon, cifs_sb, full_path,
-                                                           &reparse_tag);
-                       if (tmprc)
-                               cifs_dbg(FYI, "%s: query_reparse_tag: rc = %d\n", __func__, tmprc);
-                       if (server->ops->query_symlink) {
-                               tmprc = server->ops->query_symlink(xid, tcon, cifs_sb, full_path,
-                                                                  &data->symlink_target,
-                                                                  is_reparse_point);
-                               if (tmprc)
-                                       cifs_dbg(FYI, "%s: query_symlink: rc = %d\n", __func__,
-                                                tmprc);
-                       }
+               if (cifs_open_data_reparse(data)) {
+                       rc = query_reparse(data, sb, xid, tcon,
+                                          full_path, &fattr);
                }
-               cifs_open_info_to_fattr(&fattr, data, sb, adjust_tz, is_reparse_point, reparse_tag);
+               if (!rc)
+                       cifs_open_info_to_fattr(&fattr, data, sb);
                break;
        case -EREMOTE:
                /* DFS link, no metadata available on this server */
@@ -1168,9 +1211,7 @@ smb311_posix_get_inode_info(struct inode **inode,
        struct cifs_tcon *tcon;
        struct tcon_link *tlink;
        struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-       bool adjust_tz = false;
        struct cifs_fattr fattr = {0};
-       bool symlink = false;
        struct cifs_open_info_data data = {};
        struct cifs_sid owner, group;
        int rc = 0;
@@ -1190,9 +1231,9 @@ smb311_posix_get_inode_info(struct inode **inode,
                goto out;
        }
 
-       rc = smb311_posix_query_path_info(xid, tcon, cifs_sb, full_path, &data,
-                                         &owner, &group, &adjust_tz,
-                                         &symlink);
+       rc = smb311_posix_query_path_info(xid, tcon, cifs_sb,
+                                         full_path, &data,
+                                         &owner, &group);
 
        /*
         * 2. Convert it to internal cifs metadata (fattr)
@@ -1200,8 +1241,7 @@ smb311_posix_get_inode_info(struct inode **inode,
 
        switch (rc) {
        case 0:
-               smb311_posix_info_to_fattr(&fattr, &data, &owner, &group,
-                                          sb, adjust_tz, symlink);
+               smb311_posix_info_to_fattr(&fattr, &data, &owner, &group, sb);
                break;
        case -EREMOTE:
                /* DFS link, no metadata available on this server */