From cf77c0d8b718f49093cf1f8aa7644e4f395b6cc9 Mon Sep 17 00:00:00 2001 From: zhangkaixiang Date: Sun, 9 Jul 2023 20:46:40 +0800 Subject: [PATCH] add ioctl function to create symlink Signed-off-by: zhangkaixiang --- fs/hmdfs/file_merge.c | 60 ++++++++++++ fs/hmdfs/hmdfs.h | 8 ++ fs/hmdfs/hmdfs_dentryfile.c | 28 +++++- fs/hmdfs/hmdfs_server.c | 176 ++++++++++++++++++++++++++++++++++-- fs/hmdfs/hmdfs_trace.h | 2 + fs/hmdfs/inode_local.c | 54 +++++++++++ fs/hmdfs/inode_merge.c | 3 +- fs/hmdfs/inode_remote.c | 8 +- fs/hmdfs/stash.c | 2 +- 9 files changed, 323 insertions(+), 18 deletions(-) diff --git a/fs/hmdfs/file_merge.c b/fs/hmdfs/file_merge.c index 8048a203a8e6..bf68958aa128 100644 --- a/fs/hmdfs/file_merge.c +++ b/fs/hmdfs/file_merge.c @@ -582,11 +582,70 @@ static long hmdfs_ioc_get_writeopen_cnt(struct file *filp, unsigned long arg) return put_user(wo_cnt, (int __user *)arg); } +static long hmdfs_ioc_get_dst_path(struct file *filp, unsigned long arg) +{ + int error = 0; + char localPath[NAME_MAX]; + char distributedPath[NAME_MAX]; + struct dentry *dentry; + struct path path; + struct hmdfs_dst_info hdi; + + if (!access_ok((struct hmdfs_dst_info __user *)arg, + sizeof(struct hmdfs_dst_info))) + { + hmdfs_info("hmdfs_ioc_get_dst_path access_ok error 1"); + return -EFAULT; + } + + if (copy_from_user(&hdi, (struct hmdfs_dst_info __user *)arg, + sizeof(hdi))) + { + hmdfs_info("hmdfs_ioc_get_dst_path copy_from_user error 1"); + return -EFAULT; + } + + if (!access_ok((char *)hdi.localPath, hdi.localLen)) + { + hmdfs_info("hmdfs_ioc_get_dst_path access_ok error 2"); + return -EFAULT; + } + if (!access_ok((char *)hdi.distributedPath, hdi.distributedLen)) + { + hmdfs_info("hmdfs_ioc_get_dst_path access_ok error 3"); + return -EFAULT; + } + if (copy_from_user(localPath, (char __user *)hdi.localPath, + hdi.localLen)) + { + hmdfs_info("hmdfs_ioc_get_dst_path copy_from_user error 2"); + return -EFAULT; + } + if (copy_from_user(distributedPath, (char __user *)hdi.distributedPath, + hdi.distributedLen)) + { + hmdfs_info("hmdfs_ioc_get_dst_path copy_from_user error 3"); + return -EFAULT; + } + + dentry = kern_path_create(AT_FDCWD, distributedPath, &path, 0); + if (IS_ERR(dentry)) + { + hmdfs_info("hmdfs_ioc_get_dst_path kern_path_create error"); + return PTR_ERR(dentry); + } + error = vfs_symlink(path.dentry->d_inode, dentry, localPath); + done_path_create(&path, dentry); + return error; +} + static long hmdfs_file_ioctl_merge(struct file *filp, unsigned int cmd, unsigned long arg) { switch (cmd) { case HMDFS_IOC_GET_WRITEOPEN_CNT: return hmdfs_ioc_get_writeopen_cnt(filp, arg); + case HMDFS_IOC_GET_DST_PATH: + return hmdfs_ioc_get_dst_path(filp, arg); default: return -ENOTTY; } @@ -606,6 +665,7 @@ const struct file_operations hmdfs_file_fops_merge = { .release = hmdfs_file_release_local, .fsync = hmdfs_fsync_local, .unlocked_ioctl = hmdfs_file_ioctl_merge, + .compat_ioctl = hmdfs_file_ioctl_merge, .splice_read = generic_file_splice_read, .splice_write = iter_file_splice_write, }; diff --git a/fs/hmdfs/hmdfs.h b/fs/hmdfs/hmdfs.h index 14adb4fac1b7..d7d2d1b0591e 100644 --- a/fs/hmdfs/hmdfs.h +++ b/fs/hmdfs/hmdfs.h @@ -30,6 +30,7 @@ #define HMDFS_IOC 0xf2 #define HMDFS_IOC_SET_SHARE_PATH _IOW(HMDFS_IOC, 1, struct hmdfs_share_control) #define HMDFS_IOC_GET_WRITEOPEN_CNT _IOR(HMDFS_IOC, 2, __u32) +#define HMDFS_IOC_GET_DST_PATH _IOR(HMDFS_IOC, 3, __u32) #define HMDFS_PAGE_SIZE 4096 #define HMDFS_PAGE_OFFSET 12 @@ -222,6 +223,13 @@ static inline bool hmdfs_is_stash_enabled(const struct hmdfs_sb_info *sbi) return sbi->s_offline_stash; } +struct hmdfs_dst_info{ + uint64_t localLen; + uint64_t localPath; + uint64_t distributedLen; + uint64_t distributedPath; +}; + struct setattr_info { loff_t size; unsigned int valid; diff --git a/fs/hmdfs/hmdfs_dentryfile.c b/fs/hmdfs/hmdfs_dentryfile.c index 01efcba2f6ce..1b96ed8db248 100644 --- a/fs/hmdfs/hmdfs_dentryfile.c +++ b/fs/hmdfs/hmdfs_dentryfile.c @@ -579,6 +579,9 @@ int read_dentry(struct hmdfs_sb_info *sbi, char *file_name, else if (S_ISREG(le16_to_cpu( dentry_group->nsl[j].i_mode))) file_type = DT_REG; + else if (S_ISLNK(le16_to_cpu( + dentry_group->nsl[j].i_mode))) + file_type = DT_LNK; else continue; @@ -770,17 +773,25 @@ struct hmdfs_dentry *hmdfs_find_dentry(struct dentry *child_dentry, } void update_dentry(struct hmdfs_dentry_group *d, struct dentry *child_dentry, - struct inode *inode, __u32 name_hash, unsigned int bit_pos) + struct inode *inode, struct super_block *hmdfs_sb, + __u32 name_hash, unsigned int bit_pos) { struct hmdfs_dentry *de; + struct hmdfs_dentry_info *gdi; const struct qstr name = child_dentry->d_name; int slots = get_dentry_slots(name.len); int i; unsigned long ino; __u32 igen; - ino = inode->i_ino; - igen = inode->i_generation; + gdi = hmdfs_sb == child_dentry->d_sb ? hmdfs_d(child_dentry) : NULL; + if (!gdi && S_ISLNK(d_inode(child_dentry)->i_mode)) { + ino = d_inode(child_dentry)->i_ino; + igen = d_inode(child_dentry)->i_generation; + } else { + ino = inode->i_ino; + igen = inode->i_generation; + } de = &d->nsl[bit_pos]; de->hash = cpu_to_le32(name_hash); @@ -791,7 +802,13 @@ void update_dentry(struct hmdfs_dentry_group *d, struct dentry *child_dentry, de->i_size = cpu_to_le64(inode->i_size); de->i_ino = cpu_to_le64(generate_u64_ino(ino, igen)); de->i_flag = 0; - de->i_mode = cpu_to_le16(inode->i_mode); + + if (gdi && hm_islnk(gdi->file_type)) + de->i_mode = cpu_to_le16(S_IFLNK); + else if (!gdi && S_ISLNK(d_inode(child_dentry)->i_mode)) + de->i_mode = d_inode(child_dentry)->i_mode; + else + de->i_mode = cpu_to_le16(inode->i_mode); for (i = 0; i < slots; i++) { __set_bit_le(bit_pos + i, d->bitmap); @@ -902,7 +919,8 @@ int create_dentry(struct dentry *child_dentry, struct inode *inode, goto find; add: pos = get_dentry_group_pos(bidx); - update_dentry(dentry_blk, child_dentry, inode, namehash, bit_pos); + update_dentry(dentry_blk, child_dentry, inode, sbi->sb, namehash, + bit_pos); size = cache_file_write(sbi, file, dentry_blk, sizeof(struct hmdfs_dentry_group), &pos); if (size != sizeof(struct hmdfs_dentry_group)) diff --git a/fs/hmdfs/hmdfs_server.c b/fs/hmdfs/hmdfs_server.c index 7e7460a7be6a..1eaf72bc4f61 100644 --- a/fs/hmdfs/hmdfs_server.c +++ b/fs/hmdfs/hmdfs_server.c @@ -73,6 +73,39 @@ void remove_file_from_conn(struct hmdfs_peer *conn, __u32 file_id) spin_unlock(lock); } +struct file *hmdfs_open_link(struct hmdfs_sb_info *sbi, + const char *path) +{ + struct file *file; + int err; + const char *root_name = sbi->local_dst; + char *real_path; + int path_len; + + path_len = strlen(root_name) + strlen(path) + 2; + if (path_len > PATH_MAX) { + err = -EINVAL; + return ERR_PTR(err); + } + real_path = kzalloc(path_len, GFP_KERNEL); + if (!real_path) { + err = -ENOMEM; + return ERR_PTR(err); + } + + sprintf(real_path, "%s%s", root_name, path); + file = filp_open(real_path, O_RDWR | O_LARGEFILE, 0644); + if (IS_ERR(file)) { + hmdfs_info("filp_open failed: %ld", PTR_ERR(file)); + } else { + hmdfs_info("get file with magic %lu", + file->f_inode->i_sb->s_magic); + } + + kfree(real_path); + return file; +} + struct file *hmdfs_open_path(struct hmdfs_sb_info *sbi, const char *path) { struct path root_path; @@ -157,6 +190,38 @@ void __init hmdfs_server_add_node_evt_cb(void) hmdfs_node_add_evt_cb(server_cb, ARRAY_SIZE(server_cb)); } +static int hmdfs_get_inode_by_name(struct hmdfs_peer *con, const char *filename, + uint64_t *ino) +{ + int ret = 0; + struct path root_path; + struct path dst_path; + struct inode *inode = NULL; + + ret = kern_path(con->sbi->local_dst, 0, &root_path); + if (ret) { + hmdfs_err("kern_path failed err = %d", ret); + return ret; + } + + ret = vfs_path_lookup(root_path.dentry, root_path.mnt, filename, 0, + &dst_path); + if (ret) { + path_put(&root_path); + return ret; + } + + inode = d_inode(dst_path.dentry); + if (con->sbi->sb == inode->i_sb) + inode = hmdfs_i(inode)->lower_inode; + *ino = generate_u64_ino(inode->i_ino, inode->i_generation); + + path_put(&dst_path); + path_put(&root_path); + + return 0; +} + static const char *datasl_str[] = { "s0", "s1", "s2", "s3", "s4" }; @@ -249,7 +314,11 @@ static struct file *hmdfs_open_file(struct hmdfs_peer *con, if (err) return ERR_PTR(err); } - file = hmdfs_open_path(con->sbi, filename); + + if (hm_islnk(file_type)) + file = hmdfs_open_link(con->sbi, filename); + else + file = hmdfs_open_path(con->sbi, filename); if (IS_ERR(file)) { reset_item_opened_status(con->sbi, filename); @@ -402,8 +471,14 @@ static int hmdfs_get_open_info(struct hmdfs_peer *con, uint8_t file_type, info->stat_valid = true; } - info->real_ino = generate_u64_ino(info->inode->i_ino, - info->inode->i_generation); + if (hm_islnk(file_type)) { + ret = hmdfs_get_inode_by_name(con, filename, &info->real_ino); + if (ret) + return ret; + } else { + info->real_ino = generate_u64_ino(info->inode->i_ino, + info->inode->i_generation); + } return 0; } @@ -477,7 +552,8 @@ static int hmdfs_check_and_create(struct path *path_parent, } else { if (is_excl) err = -EEXIST; - else if (S_ISLNK(d_inode(dentry)->i_mode)) + else if (S_ISREG(d_inode(dentry)->i_mode) && + hm_islnk(hmdfs_d(dentry)->file_type)) err = -EINVAL; else if (S_ISDIR(d_inode(dentry)->i_mode)) err = -EISDIR; @@ -1149,6 +1225,50 @@ void hmdfs_server_rename(struct hmdfs_peer *con, struct hmdfs_head_cmd *cmd, hmdfs_send_err_response(con, cmd, err); } +static int hmdfs_lookup_symlink(struct path *link_path, const char *path_fmt, + ... ) +{ + int ret; + va_list args; + char *path = kmalloc(PATH_MAX, GFP_KERNEL); + + if (!path) + return -ENOMEM; + + va_start(args, path_fmt); + ret = vsnprintf(path, PATH_MAX, path_fmt, args); + va_end(args); + + if(ret >= PATH_MAX) { + ret = -ENAMETOOLONG; + goto out; + } + + ret = kern_path(path, LOOKUP_FOLLOW, link_path); + if (ret) { + hmdfs_err("kern_path failed err = %d", ret); + goto out; + } + + if (!S_ISREG(d_inode(link_path->dentry)->i_mode)) { + hmdfs_err("path is dir symlink"); + path_put(link_path); + ret = -EOPNOTSUPP; + goto out; + } + +out: + kfree(path); + return ret; +} + +struct dir_entry_info { + struct list_head list; + char *name; + int name_len; + unsigned int d_type; +}; + static int hmdfs_filldir_real(struct dir_context *ctx, const char *name, int name_len, loff_t offset, u64 ino, unsigned int d_type) @@ -1189,6 +1309,21 @@ static int hmdfs_filldir_real(struct dir_context *ctx, const char *name, if (d_type == DT_REG || d_type == DT_DIR) { create_dentry(child, d_inode(child), gc->file, gc->sbi); gc->num++; + } else if (d_type == DT_LNK) { + struct path link_path; + + res = hmdfs_lookup_symlink(&link_path, "%s/%s/%s", + gc->sbi->local_src, gc->dir, + name); + if (!res) { + create_dentry(child, d_inode(link_path.dentry), + gc->file, gc->sbi); + path_put(&link_path); + gc->num++; + } else if (res == -ENOENT) { + create_dentry(child, d_inode(child), gc->file, gc->sbi); + gc->num++; + } } dput(child); @@ -1315,6 +1450,27 @@ void hmdfs_server_writepage(struct hmdfs_peer *con, struct hmdfs_head_cmd *cmd, hmdfs_server_check_writeback(hswb); } +static int hmdfs_lookup_linkpath(struct hmdfs_sb_info *sbi, + const char *path_name, struct path *dst_path) +{ + struct path link_path; + int err; + + err = hmdfs_lookup_symlink(&link_path, "%s/%s", sbi->local_dst, + path_name); + if (err) + return err; + + if (d_inode(link_path.dentry)->i_sb != sbi->sb) { + path_put(dst_path); + *dst_path = link_path; + } else { + path_put(&link_path); + } + + return 0; +} + static struct inode *hmdfs_verify_path(struct dentry *dentry, char *recv_buf, struct super_block *sb) { @@ -1382,8 +1538,11 @@ void hmdfs_server_setattr(struct hmdfs_peer *con, struct hmdfs_head_cmd *cmd, } if (S_ISLNK(inode->i_mode)) { - err = -EPERM; - goto out_put_dst; + err = hmdfs_lookup_linkpath(con->sbi, recv->buf, &dst_path); + if(err == -ENOENT) + err = 0; + else if (err) + goto out_put_dst; } dentry = dst_path.dentry; @@ -1476,8 +1635,9 @@ void hmdfs_server_getattr(struct hmdfs_peer *con, struct hmdfs_head_cmd *cmd, } if (S_ISLNK(inode->i_mode)) { - err = -EPERM; - goto out_put_dst; + err = hmdfs_lookup_linkpath(con->sbi, recv->buf, &dst_path); + if(err && err != -ENOENT) + goto out_put_dst; } err = vfs_getattr(&dst_path, &ks, STATX_BASIC_STATS | STATX_BTIME, 0); diff --git a/fs/hmdfs/hmdfs_trace.h b/fs/hmdfs/hmdfs_trace.h index 86478425aea8..c12b7fa5cf42 100644 --- a/fs/hmdfs/hmdfs_trace.h +++ b/fs/hmdfs/hmdfs_trace.h @@ -203,6 +203,8 @@ define_hmdfs_lookup_op_end_event(hmdfs_mkdir_merge); define_hmdfs_lookup_op_end_event(hmdfs_rmdir_merge); define_hmdfs_lookup_op_end_event(hmdfs_create_merge); +define_hmdfs_lookup_op_end_event(hmdfs_get_link_local); + define_hmdfs_lookup_op_end_event(hmdfs_lookup_share); define_hmdfs_lookup_op_end_event(hmdfs_lookup_share_end); diff --git a/fs/hmdfs/inode_local.c b/fs/hmdfs/inode_local.c index 5d26f6dbbd02..aca355d33928 100644 --- a/fs/hmdfs/inode_local.c +++ b/fs/hmdfs/inode_local.c @@ -103,6 +103,9 @@ struct inode *fill_inode_local(struct super_block *sb, else if (S_ISREG(lower_inode->i_mode)) inode->i_mode = (lower_inode->i_mode & S_IFMT) | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + else if (S_ISLNK(lower_inode->i_mode)) + inode->i_mode = + S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; #ifdef CONFIG_HMDFS_FS_PERMISSION inode->i_uid = lower_inode->i_uid; @@ -124,6 +127,10 @@ struct inode *fill_inode_local(struct super_block *sb, } else if (S_ISREG(lower_inode->i_mode)) { inode->i_op = &hmdfs_file_iops_local; inode->i_fop = &hmdfs_file_fops_local; + } else if (S_ISLNK(lower_inode->i_mode)) { + inode->i_op = &hmdfs_symlink_iops_local; + inode->i_fop = &hmdfs_file_fops_local; + inode->i_size = i_size_read(lower_inode); } else { ret = -EIO; goto bad_inode; @@ -217,6 +224,11 @@ static int __lookup_nosensitive(struct path *lower_parent_path, return err; } +static inline void set_symlink_flag(struct hmdfs_dentry_info *gdi) +{ + gdi->file_type = HM_SYMLINK; +} + struct dentry *hmdfs_lookup_local(struct inode *parent_inode, struct dentry *child_dentry, unsigned int flags) @@ -262,6 +274,8 @@ struct dentry *hmdfs_lookup_local(struct inode *parent_inode, d_inode(lower_path.dentry), child_dentry->d_name.name); + if (S_ISLNK(d_inode(lower_path.dentry)->i_mode)) + set_symlink_flag(gdi); if (IS_ERR(child_inode)) { err = PTR_ERR(child_inode); ret = ERR_PTR(err); @@ -726,6 +740,40 @@ int hmdfs_rename_local(struct inode *old_dir, struct dentry *old_dentry, return err; } +static const char *hmdfs_get_link_local(struct dentry *dentry, + struct inode *inode, + struct delayed_call *done) +{ + const char *link = NULL; + struct dentry *lower_dentry = NULL; + struct inode *lower_inode = NULL; + struct path lower_path; + + if(!dentry) { + hmdfs_err("dentry MULL"); + link = ERR_PTR(-ECHILD); + goto link_out; + } + + hmdfs_get_lower_path(dentry, &lower_path); + lower_dentry = lower_path.dentry; + lower_inode = d_inode(lower_dentry); + if(!lower_inode->i_op || !lower_inode->i_op->get_link) { + hmdfs_err("The lower inode doesn't support get_link i_op"); + link = ERR_PTR(-EINVAL); + goto out; + } + + link = lower_inode->i_op->get_link(lower_dentry, lower_inode, done); + if(IS_ERR_OR_NULL(link)) + goto out; + fsstack_copy_attr_atime(inode, lower_inode); +out: + hmdfs_put_lower_path(&lower_path); +link_out: + return link; +} + static int hmdfs_setattr_local(struct dentry *dentry, struct iattr *ia) { struct inode *inode = d_inode(dentry); @@ -899,6 +947,12 @@ const struct inode_operations hmdfs_dir_inode_ops_local = { .getattr = hmdfs_getattr_local, }; +const struct inode_operations hmdfs_symlink_iops_local = { + .get_link = hmdfs_get_link_local, + .permission = hmdfs_permission, + .setattr = hmdfs_setattr_local, +}; + const struct inode_operations hmdfs_dir_inode_ops_share = { .lookup = hmdfs_lookup_share, .permission = hmdfs_permission, diff --git a/fs/hmdfs/inode_merge.c b/fs/hmdfs/inode_merge.c index 17a7af217d8d..26a2405f373f 100644 --- a/fs/hmdfs/inode_merge.c +++ b/fs/hmdfs/inode_merge.c @@ -109,9 +109,8 @@ static struct inode *fill_inode_merge(struct super_block *sb, if (lo_d_dentry) { fst_lo_d = lo_d_dentry; dget(fst_lo_d); - } else { + } else fst_lo_d = hmdfs_get_fst_lo_d(child_dentry); - } if (!fst_lo_d) { inode = ERR_PTR(-EINVAL); goto out; diff --git a/fs/hmdfs/inode_remote.c b/fs/hmdfs/inode_remote.c index 2670e3e81891..5efeae398d23 100644 --- a/fs/hmdfs/inode_remote.c +++ b/fs/hmdfs/inode_remote.c @@ -375,12 +375,14 @@ struct inode *fill_inode_remote(struct super_block *sb, struct hmdfs_peer *con, inode->i_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IXOTH; else if (S_ISREG(mode)) inode->i_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP; + else if (S_ISLNK(mode)) + inode->i_mode = S_IFREG | S_IRWXU | S_IRWXG; else { ret = -EIO; goto bad_inode; } - if (S_ISREG(mode)) { + if (S_ISREG(mode) || S_ISLNK(mode)) { inode->i_op = con->conn_operations->remote_file_iops; inode->i_fop = con->conn_operations->remote_file_fops; inode->i_size = res->i_size; @@ -446,7 +448,9 @@ static struct dentry *hmdfs_lookup_remote_dentry(struct inode *parent_inode, lookup_result = hmdfs_lookup_by_con(con, child_dentry, &qstr, flags, relative_path); if (lookup_result != NULL) { - if (in_share_dir(child_dentry)) + if (S_ISLNK(lookup_result->i_mode)) + gdi->file_type = HM_SYMLINK; + else if (in_share_dir(child_dentry)) gdi->file_type = HM_SHARE; inode = fill_inode_remote(sb, con, lookup_result, parent_inode); ret = d_splice_alias(inode, child_dentry); diff --git a/fs/hmdfs/stash.c b/fs/hmdfs/stash.c index 413720404cc7..c320af7f60e0 100644 --- a/fs/hmdfs/stash.c +++ b/fs/hmdfs/stash.c @@ -2179,7 +2179,7 @@ hmdfs_need_rebuild_inode_stash_status(struct hmdfs_peer *conn, umode_t mode) { return hmdfs_is_stash_enabled(conn->sbi) && READ_ONCE(conn->need_rebuild_stash_list) && - S_ISREG(mode); + (S_ISREG(mode) || S_ISLNK(mode)); } void hmdfs_remote_init_stash_status(struct hmdfs_peer *conn, -- Gitee