xref: /openbmc/linux/fs/kernfs/symlink.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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