Blame view

fs/kernfs/symlink.c 3.31 KB
55716d264   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-only
b8441ed27   Tejun Heo   sysfs, kernfs: ad...
2
3
4
5
6
7
  /*
   * fs/kernfs/symlink.c - kernfs symlink implementation
   *
   * Copyright (c) 2001-3 Patrick Mochel
   * Copyright (c) 2007 SUSE Linux Products GmbH
   * Copyright (c) 2007, 2013 Tejun Heo <tj@kernel.org>
b8441ed27   Tejun Heo   sysfs, kernfs: ad...
8
   */
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  
  #include <linux/fs.h>
  #include <linux/gfp.h>
  #include <linux/namei.h>
  
  #include "kernfs-internal.h"
  
  /**
   * kernfs_create_link - create a symlink
   * @parent: directory to create the symlink in
   * @name: name of the symlink
   * @target: target node for the symlink to point to
   *
   * Returns the created node on success, ERR_PTR() value on error.
488dee96b   Dmitry Torokhov   kernfs: allow cre...
23
   * Ownership of the link matches ownership of the target.
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
24
   */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
25
26
27
  struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
  				       const char *name,
  				       struct kernfs_node *target)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
28
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
29
  	struct kernfs_node *kn;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
30
  	int error;
488dee96b   Dmitry Torokhov   kernfs: allow cre...
31
32
  	kuid_t uid = GLOBAL_ROOT_UID;
  	kgid_t gid = GLOBAL_ROOT_GID;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
33

488dee96b   Dmitry Torokhov   kernfs: allow cre...
34
  	if (target->iattr) {
058952196   Ondrej Mosnacek   kernfs: clean up ...
35
36
  		uid = target->iattr->ia_uid;
  		gid = target->iattr->ia_gid;
488dee96b   Dmitry Torokhov   kernfs: allow cre...
37
38
39
40
  	}
  
  	kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, uid, gid,
  			     KERNFS_LINK);
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
41
  	if (!kn)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
42
  		return ERR_PTR(-ENOMEM);
ac9bba031   Tejun Heo   sysfs, kernfs: im...
43
  	if (kernfs_ns_enabled(parent))
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
44
45
  		kn->ns = target->ns;
  	kn->symlink.target_kn = target;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
46
  	kernfs_get(target);	/* ref owned by symlink */
988cd7afb   Tejun Heo   kernfs: remove ke...
47
  	error = kernfs_add_one(kn);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
48
  	if (!error)
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
49
  		return kn;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
50

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
51
  	kernfs_put(kn);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
52
53
  	return ERR_PTR(error);
  }
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
54
55
  static int kernfs_get_target_path(struct kernfs_node *parent,
  				  struct kernfs_node *target, char *path)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
56
  {
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
57
  	struct kernfs_node *base, *kn;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
58
59
60
61
  	char *s = path;
  	int len = 0;
  
  	/* go up to the root, stop at the base */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
62
  	base = parent;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
63
64
65
66
  	while (base->parent) {
  		kn = target->parent;
  		while (kn->parent && base != kn)
  			kn = kn->parent;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
67

324a56e16   Tejun Heo   kernfs: s/sysfs_d...
68
  		if (base == kn)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
69
  			break;
a75e78f21   Bernd Edlinger   kernfs: Fix range...
70
71
  		if ((s - path) + 3 >= PATH_MAX)
  			return -ENAMETOOLONG;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
72
73
  		strcpy(s, "../");
  		s += 3;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
74
  		base = base->parent;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
75
76
77
  	}
  
  	/* determine end of target string for reverse fillup */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
78
  	kn = target;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
79
80
81
  	while (kn->parent && kn != base) {
  		len += strlen(kn->name) + 1;
  		kn = kn->parent;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
82
83
84
85
86
87
  	}
  
  	/* check limits */
  	if (len < 2)
  		return -EINVAL;
  	len--;
a75e78f21   Bernd Edlinger   kernfs: Fix range...
88
  	if ((s - path) + len >= PATH_MAX)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
89
90
91
  		return -ENAMETOOLONG;
  
  	/* reverse fillup of target string from target to base */
324a56e16   Tejun Heo   kernfs: s/sysfs_d...
92
  	kn = target;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
93
94
  	while (kn->parent && kn != base) {
  		int slen = strlen(kn->name);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
95
96
  
  		len -= slen;
166126c1e   Guenter Roeck   kernfs: Replace s...
97
  		memcpy(s + len, kn->name, slen);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
98
99
  		if (len)
  			s[--len] = '/';
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
100
  		kn = kn->parent;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
101
102
103
104
  	}
  
  	return 0;
  }
319ba91d3   Shaohua Li   kernfs: don't set...
105
  static int kernfs_getlink(struct inode *inode, char *path)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
106
  {
319ba91d3   Shaohua Li   kernfs: don't set...
107
  	struct kernfs_node *kn = inode->i_private;
adc5e8b58   Tejun Heo   kernfs: drop s_ p...
108
109
  	struct kernfs_node *parent = kn->parent;
  	struct kernfs_node *target = kn->symlink.target_kn;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
110
  	int error;
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
111
  	mutex_lock(&kernfs_mutex);
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
112
  	error = kernfs_get_target_path(parent, target, path);
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
113
  	mutex_unlock(&kernfs_mutex);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
114
115
116
  
  	return error;
  }
6b2553918   Al Viro   replace ->follow_...
117
  static const char *kernfs_iop_get_link(struct dentry *dentry,
fceef393a   Al Viro   switch ->get_link...
118
119
  				       struct inode *inode,
  				       struct delayed_call *done)
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
120
  {
fceef393a   Al Viro   switch ->get_link...
121
122
  	char *body;
  	int error;
6b2553918   Al Viro   replace ->follow_...
123
124
125
  
  	if (!dentry)
  		return ERR_PTR(-ECHILD);
fceef393a   Al Viro   switch ->get_link...
126
127
  	body = kzalloc(PAGE_SIZE, GFP_KERNEL);
  	if (!body)
680baacbc   Al Viro   new ->follow_link...
128
  		return ERR_PTR(-ENOMEM);
319ba91d3   Shaohua Li   kernfs: don't set...
129
  	error = kernfs_getlink(inode, body);
680baacbc   Al Viro   new ->follow_link...
130
  	if (unlikely(error < 0)) {
fceef393a   Al Viro   switch ->get_link...
131
  		kfree(body);
680baacbc   Al Viro   new ->follow_link...
132
  		return ERR_PTR(error);
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
133
  	}
fceef393a   Al Viro   switch ->get_link...
134
135
  	set_delayed_call(done, kfree_link, body);
  	return body;
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
136
  }
a797bfc30   Tejun Heo   kernfs: s/sysfs/k...
137
  const struct inode_operations kernfs_symlink_iops = {
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
138
  	.listxattr	= kernfs_iop_listxattr,
6b2553918   Al Viro   replace ->follow_...
139
  	.get_link	= kernfs_iop_get_link,
c637b8acb   Tejun Heo   kernfs: s/sysfs/k...
140
141
142
  	.setattr	= kernfs_iop_setattr,
  	.getattr	= kernfs_iop_getattr,
  	.permission	= kernfs_iop_permission,
2072f1afd   Tejun Heo   sysfs, kernfs: mo...
143
  };