155716d26SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b8441ed2STejun Heo /*
3b8441ed2STejun Heo * fs/kernfs/symlink.c - kernfs symlink implementation
4b8441ed2STejun Heo *
5b8441ed2STejun Heo * Copyright (c) 2001-3 Patrick Mochel
6b8441ed2STejun Heo * Copyright (c) 2007 SUSE Linux Products GmbH
7b8441ed2STejun Heo * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org>
8b8441ed2STejun Heo */
92072f1afSTejun Heo
102072f1afSTejun Heo #include <linux/fs.h>
112072f1afSTejun Heo #include <linux/gfp.h>
122072f1afSTejun Heo #include <linux/namei.h>
132072f1afSTejun Heo
142072f1afSTejun Heo #include "kernfs-internal.h"
152072f1afSTejun Heo
162072f1afSTejun Heo /**
172072f1afSTejun Heo * kernfs_create_link - create a symlink
182072f1afSTejun Heo * @parent: directory to create the symlink in
192072f1afSTejun Heo * @name: name of the symlink
202072f1afSTejun Heo * @target: target node for the symlink to point to
212072f1afSTejun Heo *
22*24b3e3ddSRandy Dunlap * Return: the created node on success, ERR_PTR() value on error.
23488dee96SDmitry Torokhov * Ownership of the link matches ownership of the target.
242072f1afSTejun Heo */
kernfs_create_link(struct kernfs_node * parent,const char * name,struct kernfs_node * target)25324a56e1STejun Heo struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
262072f1afSTejun Heo const char *name,
27324a56e1STejun Heo struct kernfs_node *target)
282072f1afSTejun Heo {
29324a56e1STejun Heo struct kernfs_node *kn;
302072f1afSTejun Heo int error;
31488dee96SDmitry Torokhov kuid_t uid = GLOBAL_ROOT_UID;
32488dee96SDmitry Torokhov kgid_t gid = GLOBAL_ROOT_GID;
332072f1afSTejun Heo
34488dee96SDmitry Torokhov if (target->iattr) {
3505895219SOndrej Mosnacek uid = target->iattr->ia_uid;
3605895219SOndrej Mosnacek gid = target->iattr->ia_gid;
37488dee96SDmitry Torokhov }
38488dee96SDmitry Torokhov
398f5cfb3bSLuis Chamberlain kn = kernfs_new_node(parent, name, S_IFLNK|0777, uid, gid, KERNFS_LINK);
40324a56e1STejun Heo if (!kn)
412072f1afSTejun Heo return ERR_PTR(-ENOMEM);
422072f1afSTejun Heo
43ac9bba03STejun Heo if (kernfs_ns_enabled(parent))
44adc5e8b5STejun Heo kn->ns = target->ns;
45adc5e8b5STejun Heo kn->symlink.target_kn = target;
462072f1afSTejun Heo kernfs_get(target); /* ref owned by symlink */
472072f1afSTejun Heo
48988cd7afSTejun Heo error = kernfs_add_one(kn);
492072f1afSTejun Heo if (!error)
50324a56e1STejun Heo return kn;
512072f1afSTejun Heo
52324a56e1STejun Heo kernfs_put(kn);
532072f1afSTejun Heo return ERR_PTR(error);
542072f1afSTejun Heo }
552072f1afSTejun Heo
kernfs_get_target_path(struct kernfs_node * parent,struct kernfs_node * target,char * path)56c637b8acSTejun Heo static int kernfs_get_target_path(struct kernfs_node *parent,
57324a56e1STejun Heo struct kernfs_node *target, char *path)
582072f1afSTejun Heo {
59324a56e1STejun Heo struct kernfs_node *base, *kn;
602072f1afSTejun Heo char *s = path;
612072f1afSTejun Heo int len = 0;
622072f1afSTejun Heo
632072f1afSTejun Heo /* go up to the root, stop at the base */
64324a56e1STejun Heo base = parent;
65adc5e8b5STejun Heo while (base->parent) {
66adc5e8b5STejun Heo kn = target->parent;
67adc5e8b5STejun Heo while (kn->parent && base != kn)
68adc5e8b5STejun Heo kn = kn->parent;
692072f1afSTejun Heo
70324a56e1STejun Heo if (base == kn)
712072f1afSTejun Heo break;
722072f1afSTejun Heo
73a75e78f2SBernd Edlinger if ((s - path) + 3 >= PATH_MAX)
74a75e78f2SBernd Edlinger return -ENAMETOOLONG;
75a75e78f2SBernd Edlinger
762072f1afSTejun Heo strcpy(s, "../");
772072f1afSTejun Heo s += 3;
78adc5e8b5STejun Heo base = base->parent;
792072f1afSTejun Heo }
802072f1afSTejun Heo
812072f1afSTejun Heo /* determine end of target string for reverse fillup */
82324a56e1STejun Heo kn = target;
83adc5e8b5STejun Heo while (kn->parent && kn != base) {
84adc5e8b5STejun Heo len += strlen(kn->name) + 1;
85adc5e8b5STejun Heo kn = kn->parent;
862072f1afSTejun Heo }
872072f1afSTejun Heo
882072f1afSTejun Heo /* check limits */
892072f1afSTejun Heo if (len < 2)
902072f1afSTejun Heo return -EINVAL;
912072f1afSTejun Heo len--;
92a75e78f2SBernd Edlinger if ((s - path) + len >= PATH_MAX)
932072f1afSTejun Heo return -ENAMETOOLONG;
942072f1afSTejun Heo
952072f1afSTejun Heo /* reverse fillup of target string from target to base */
96324a56e1STejun Heo kn = target;
97adc5e8b5STejun Heo while (kn->parent && kn != base) {
98adc5e8b5STejun Heo int slen = strlen(kn->name);
992072f1afSTejun Heo
1002072f1afSTejun Heo len -= slen;
101166126c1SGuenter Roeck memcpy(s + len, kn->name, slen);
1022072f1afSTejun Heo if (len)
1032072f1afSTejun Heo s[--len] = '/';
1042072f1afSTejun Heo
105adc5e8b5STejun Heo kn = kn->parent;
1062072f1afSTejun Heo }
1072072f1afSTejun Heo
1082072f1afSTejun Heo return 0;
1092072f1afSTejun Heo }
1102072f1afSTejun Heo
kernfs_getlink(struct inode * inode,char * path)111319ba91dSShaohua Li static int kernfs_getlink(struct inode *inode, char *path)
1122072f1afSTejun Heo {
113319ba91dSShaohua Li struct kernfs_node *kn = inode->i_private;
114adc5e8b5STejun Heo struct kernfs_node *parent = kn->parent;
115adc5e8b5STejun Heo struct kernfs_node *target = kn->symlink.target_kn;
116393c3714SMinchan Kim struct kernfs_root *root = kernfs_root(parent);
1172072f1afSTejun Heo int error;
1182072f1afSTejun Heo
119393c3714SMinchan Kim down_read(&root->kernfs_rwsem);
120c637b8acSTejun Heo error = kernfs_get_target_path(parent, target, path);
121393c3714SMinchan Kim up_read(&root->kernfs_rwsem);
1222072f1afSTejun Heo
1232072f1afSTejun Heo return error;
1242072f1afSTejun Heo }
1252072f1afSTejun Heo
kernfs_iop_get_link(struct dentry * dentry,struct inode * inode,struct delayed_call * done)1266b255391SAl Viro static const char *kernfs_iop_get_link(struct dentry *dentry,
127fceef393SAl Viro struct inode *inode,
128fceef393SAl Viro struct delayed_call *done)
1292072f1afSTejun Heo {
130fceef393SAl Viro char *body;
131fceef393SAl Viro int error;
1326b255391SAl Viro
1336b255391SAl Viro if (!dentry)
1346b255391SAl Viro return ERR_PTR(-ECHILD);
135fceef393SAl Viro body = kzalloc(PAGE_SIZE, GFP_KERNEL);
136fceef393SAl Viro if (!body)
137680baacbSAl Viro return ERR_PTR(-ENOMEM);
138319ba91dSShaohua Li error = kernfs_getlink(inode, body);
139680baacbSAl Viro if (unlikely(error < 0)) {
140fceef393SAl Viro kfree(body);
141680baacbSAl Viro return ERR_PTR(error);
1422072f1afSTejun Heo }
143fceef393SAl Viro set_delayed_call(done, kfree_link, body);
144fceef393SAl Viro return body;
1452072f1afSTejun Heo }
1462072f1afSTejun Heo
147a797bfc3STejun Heo const struct inode_operations kernfs_symlink_iops = {
148c637b8acSTejun Heo .listxattr = kernfs_iop_listxattr,
1496b255391SAl Viro .get_link = kernfs_iop_get_link,
150c637b8acSTejun Heo .setattr = kernfs_iop_setattr,
151c637b8acSTejun Heo .getattr = kernfs_iop_getattr,
152c637b8acSTejun Heo .permission = kernfs_iop_permission,
1532072f1afSTejun Heo };
154