]> Git Repo - linux.git/blame - fs/kernfs/symlink.c
Merge tag 'armsoc-dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
[linux.git] / fs / kernfs / symlink.c
CommitLineData
b8441ed2
TH
1/*
2 * fs/kernfs/symlink.c - kernfs symlink implementation
3 *
4 * Copyright (c) 2001-3 Patrick Mochel
5 * Copyright (c) 2007 SUSE Linux Products GmbH
6 * Copyright (c) 2007, 2013 Tejun Heo <[email protected]>
7 *
8 * This file is released under the GPLv2.
9 */
2072f1af
TH
10
11#include <linux/fs.h>
12#include <linux/gfp.h>
13#include <linux/namei.h>
14
15#include "kernfs-internal.h"
16
17/**
18 * kernfs_create_link - create a symlink
19 * @parent: directory to create the symlink in
20 * @name: name of the symlink
21 * @target: target node for the symlink to point to
22 *
23 * Returns the created node on success, ERR_PTR() value on error.
488dee96 24 * Ownership of the link matches ownership of the target.
2072f1af 25 */
324a56e1
TH
26struct kernfs_node *kernfs_create_link(struct kernfs_node *parent,
27 const char *name,
28 struct kernfs_node *target)
2072f1af 29{
324a56e1 30 struct kernfs_node *kn;
2072f1af 31 int error;
488dee96
DT
32 kuid_t uid = GLOBAL_ROOT_UID;
33 kgid_t gid = GLOBAL_ROOT_GID;
2072f1af 34
488dee96
DT
35 if (target->iattr) {
36 uid = target->iattr->ia_iattr.ia_uid;
37 gid = target->iattr->ia_iattr.ia_gid;
38 }
39
40 kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, uid, gid,
41 KERNFS_LINK);
324a56e1 42 if (!kn)
2072f1af
TH
43 return ERR_PTR(-ENOMEM);
44
ac9bba03 45 if (kernfs_ns_enabled(parent))
adc5e8b5
TH
46 kn->ns = target->ns;
47 kn->symlink.target_kn = target;
2072f1af
TH
48 kernfs_get(target); /* ref owned by symlink */
49
988cd7af 50 error = kernfs_add_one(kn);
2072f1af 51 if (!error)
324a56e1 52 return kn;
2072f1af 53
324a56e1 54 kernfs_put(kn);
2072f1af
TH
55 return ERR_PTR(error);
56}
57
c637b8ac
TH
58static int kernfs_get_target_path(struct kernfs_node *parent,
59 struct kernfs_node *target, char *path)
2072f1af 60{
324a56e1 61 struct kernfs_node *base, *kn;
2072f1af
TH
62 char *s = path;
63 int len = 0;
64
65 /* go up to the root, stop at the base */
324a56e1 66 base = parent;
adc5e8b5
TH
67 while (base->parent) {
68 kn = target->parent;
69 while (kn->parent && base != kn)
70 kn = kn->parent;
2072f1af 71
324a56e1 72 if (base == kn)
2072f1af
TH
73 break;
74
a75e78f2
BE
75 if ((s - path) + 3 >= PATH_MAX)
76 return -ENAMETOOLONG;
77
2072f1af
TH
78 strcpy(s, "../");
79 s += 3;
adc5e8b5 80 base = base->parent;
2072f1af
TH
81 }
82
83 /* determine end of target string for reverse fillup */
324a56e1 84 kn = target;
adc5e8b5
TH
85 while (kn->parent && kn != base) {
86 len += strlen(kn->name) + 1;
87 kn = kn->parent;
2072f1af
TH
88 }
89
90 /* check limits */
91 if (len < 2)
92 return -EINVAL;
93 len--;
a75e78f2 94 if ((s - path) + len >= PATH_MAX)
2072f1af
TH
95 return -ENAMETOOLONG;
96
97 /* reverse fillup of target string from target to base */
324a56e1 98 kn = target;
adc5e8b5
TH
99 while (kn->parent && kn != base) {
100 int slen = strlen(kn->name);
2072f1af
TH
101
102 len -= slen;
166126c1 103 memcpy(s + len, kn->name, slen);
2072f1af
TH
104 if (len)
105 s[--len] = '/';
106
adc5e8b5 107 kn = kn->parent;
2072f1af
TH
108 }
109
110 return 0;
111}
112
319ba91d 113static int kernfs_getlink(struct inode *inode, char *path)
2072f1af 114{
319ba91d 115 struct kernfs_node *kn = inode->i_private;
adc5e8b5
TH
116 struct kernfs_node *parent = kn->parent;
117 struct kernfs_node *target = kn->symlink.target_kn;
2072f1af
TH
118 int error;
119
a797bfc3 120 mutex_lock(&kernfs_mutex);
c637b8ac 121 error = kernfs_get_target_path(parent, target, path);
a797bfc3 122 mutex_unlock(&kernfs_mutex);
2072f1af
TH
123
124 return error;
125}
126
6b255391 127static const char *kernfs_iop_get_link(struct dentry *dentry,
fceef393
AV
128 struct inode *inode,
129 struct delayed_call *done)
2072f1af 130{
fceef393
AV
131 char *body;
132 int error;
6b255391
AV
133
134 if (!dentry)
135 return ERR_PTR(-ECHILD);
fceef393
AV
136 body = kzalloc(PAGE_SIZE, GFP_KERNEL);
137 if (!body)
680baacb 138 return ERR_PTR(-ENOMEM);
319ba91d 139 error = kernfs_getlink(inode, body);
680baacb 140 if (unlikely(error < 0)) {
fceef393 141 kfree(body);
680baacb 142 return ERR_PTR(error);
2072f1af 143 }
fceef393
AV
144 set_delayed_call(done, kfree_link, body);
145 return body;
2072f1af
TH
146}
147
a797bfc3 148const struct inode_operations kernfs_symlink_iops = {
c637b8ac 149 .listxattr = kernfs_iop_listxattr,
6b255391 150 .get_link = kernfs_iop_get_link,
c637b8ac
TH
151 .setattr = kernfs_iop_setattr,
152 .getattr = kernfs_iop_getattr,
153 .permission = kernfs_iop_permission,
2072f1af 154};
This page took 0.273524 seconds and 4 git commands to generate.