60 |
* @buffer: Pointer to buffer to return value in. |
* @buffer: Pointer to buffer to return value in. |
61 |
* @buflen: Sizeof @buffer. |
* @buflen: Sizeof @buffer. |
62 |
* |
* |
63 |
* Returns 0 on success, -ENOMEM otherwise. |
* Returns the buffer on success, an error code otherwise. |
64 |
* |
* |
65 |
* Caller holds the dcache_lock and vfsmount_lock. |
* Caller holds the dcache_lock and vfsmount_lock. |
66 |
* Based on __d_path() in fs/dcache.c |
* Based on __d_path() in fs/dcache.c |
67 |
* |
* |
68 |
* If dentry is a directory, trailing '/' is appended. |
* If dentry is a directory, trailing '/' is appended. |
69 |
* Characters out of 0x20 < c < 0x7F range are converted to |
* /proc/pid is represented as /proc/self if pid is current. |
|
* \ooo style octal string. |
|
|
* Character \ is converted to \\ string. |
|
70 |
*/ |
*/ |
71 |
static int ccs_get_absolute_path(struct path *path, char *buffer, int buflen) |
static char *ccs_get_absolute_path(struct path *path, char * const buffer, |
72 |
|
const int buflen) |
73 |
{ |
{ |
74 |
char *start = buffer; |
char *pos = buffer + buflen - 1; |
|
char *end = buffer + buflen; |
|
75 |
struct dentry *dentry = path->dentry; |
struct dentry *dentry = path->dentry; |
76 |
struct vfsmount *vfsmnt = path->mnt; |
struct vfsmount *vfsmnt = path->mnt; |
77 |
bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)); |
bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)); |
78 |
|
const char *name; |
79 |
|
int len; |
80 |
|
|
81 |
if (buflen < 256) |
if (buflen < 256) |
82 |
goto out; |
goto out; |
83 |
|
|
84 |
*--end = '\0'; |
*pos = '\0'; |
|
buflen--; |
|
|
|
|
85 |
for (;;) { |
for (;;) { |
86 |
struct dentry *parent; |
struct dentry *parent; |
|
|
|
87 |
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
|
/* Global root? */ |
|
88 |
if (vfsmnt->mnt_parent == vfsmnt) |
if (vfsmnt->mnt_parent == vfsmnt) |
89 |
break; |
break; |
90 |
dentry = vfsmnt->mnt_mountpoint; |
dentry = vfsmnt->mnt_mountpoint; |
93 |
} |
} |
94 |
if (is_dir) { |
if (is_dir) { |
95 |
is_dir = false; |
is_dir = false; |
96 |
*--end = '/'; |
*--pos = '/'; |
|
buflen--; |
|
97 |
} |
} |
98 |
parent = dentry->d_parent; |
parent = dentry->d_parent; |
99 |
{ |
name = dentry->d_name.name; |
100 |
const char *sp = dentry->d_name.name; |
len = dentry->d_name.len; |
101 |
const char *cp = sp + dentry->d_name.len - 1; |
if (IS_ROOT(parent) && *name > '0' && *name <= '9' && |
102 |
unsigned char c; |
parent->d_sb && |
103 |
|
parent->d_sb->s_magic == PROC_SUPER_MAGIC) { |
104 |
/* |
char *ep; |
105 |
* Exception: Use /proc/self/ rather than |
const pid_t pid = (pid_t) simple_strtoul(name, &ep, |
106 |
* /proc/\$/ for current process. |
10); |
|
*/ |
|
|
if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' && |
|
|
parent->d_sb && |
|
|
parent->d_sb->s_magic == PROC_SUPER_MAGIC) { |
|
|
char *ep; |
|
|
const pid_t pid |
|
|
= (pid_t) simple_strtoul(sp, &ep, 10); |
|
107 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) |
108 |
const pid_t tgid |
const pid_t tgid = task_tgid_nr_ns(current, |
109 |
= task_tgid_nr_ns(current, |
dentry->d_sb-> |
110 |
dentry->d_sb-> |
s_fs_info); |
111 |
s_fs_info); |
if (!*ep && pid == tgid && tgid) { |
112 |
if (!*ep && pid == tgid && tgid) { |
name = "self"; |
113 |
sp = "self"; |
len = 4; |
|
cp = sp + 3; |
|
|
} |
|
|
#else |
|
|
if (!*ep && pid == sys_getpid()) { |
|
|
sp = "self"; |
|
|
cp = sp + 3; |
|
|
} |
|
|
#endif |
|
114 |
} |
} |
115 |
|
#else |
116 |
while (sp <= cp) { |
if (!*ep && pid == sys_getpid()) { |
117 |
c = *(unsigned char *) cp; |
name = "self"; |
118 |
if (c == '\\') { |
len = 4; |
|
buflen -= 2; |
|
|
if (buflen < 0) |
|
|
goto out; |
|
|
*--end = '\\'; |
|
|
*--end = '\\'; |
|
|
} else if (c > ' ' && c < 127) { |
|
|
if (--buflen < 0) |
|
|
goto out; |
|
|
*--end = (char) c; |
|
|
} else { |
|
|
buflen -= 4; |
|
|
if (buflen < 0) |
|
|
goto out; |
|
|
*--end = (c & 7) + '0'; |
|
|
*--end = ((c >> 3) & 7) + '0'; |
|
|
*--end = (c >> 6) + '0'; |
|
|
*--end = '\\'; |
|
|
} |
|
|
cp--; |
|
119 |
} |
} |
120 |
if (--buflen < 0) |
#endif |
|
goto out; |
|
|
*--end = '/'; |
|
121 |
} |
} |
122 |
|
pos -= len; |
123 |
|
if (pos <= buffer) |
124 |
|
goto out; |
125 |
|
memmove(pos, name, len); |
126 |
|
*--pos = '/'; |
127 |
dentry = parent; |
dentry = parent; |
128 |
} |
} |
129 |
if (*end == '/') { |
if (*pos == '/') |
130 |
buflen++; |
pos++; |
131 |
end++; |
len = dentry->d_name.len; |
132 |
} |
pos -= len; |
133 |
{ |
if (pos < buffer) |
134 |
const char *sp = dentry->d_name.name; |
goto out; |
135 |
const char *cp = sp + dentry->d_name.len - 1; |
memmove(pos, dentry->d_name.name, len); |
136 |
unsigned char c; |
return pos; |
|
while (sp <= cp) { |
|
|
c = *(unsigned char *) cp; |
|
|
if (c == '\\') { |
|
|
buflen -= 2; |
|
|
if (buflen < 0) |
|
|
goto out; |
|
|
*--end = '\\'; |
|
|
*--end = '\\'; |
|
|
} else if (c > ' ' && c < 127) { |
|
|
if (--buflen < 0) |
|
|
goto out; |
|
|
*--end = (char) c; |
|
|
} else { |
|
|
buflen -= 4; |
|
|
if (buflen < 0) |
|
|
goto out; |
|
|
*--end = (c & 7) + '0'; |
|
|
*--end = ((c >> 3) & 7) + '0'; |
|
|
*--end = (c >> 6) + '0'; |
|
|
*--end = '\\'; |
|
|
} |
|
|
cp--; |
|
|
} |
|
|
} |
|
|
/* Move the pathname to the top of the buffer. */ |
|
|
memmove(start, end, strlen(end) + 1); |
|
|
return 0; |
|
137 |
out: |
out: |
138 |
return -ENOMEM; |
return ERR_PTR(-ENOMEM); |
139 |
} |
} |
140 |
|
|
141 |
#define SOCKFS_MAGIC 0x534F434B |
#define SOCKFS_MAGIC 0x534F434B |
142 |
|
|
143 |
/** |
/** |
144 |
* ccs_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. |
* ccs_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
145 |
* |
* |
146 |
* @path: Pointer to "struct path". |
* @path: Pointer to "struct path". |
|
* @newname: Pointer to buffer to return value in. |
|
|
* @newname_len: Size of @newname. |
|
147 |
* |
* |
148 |
* Returns 0 on success, negative value otherwise. |
* Returns the realpath of the given @path on success, NULL otherwise. |
149 |
|
* |
150 |
|
* This function uses kzalloc(), so caller must kfree() if this function |
151 |
|
* didn't return NULL. |
152 |
*/ |
*/ |
153 |
static int ccs_realpath_from_path2(struct path *path, char *newname, |
char *ccs_realpath_from_path(struct path *path) |
|
int newname_len) |
|
154 |
{ |
{ |
155 |
int error = -EINVAL; |
char *buf = NULL; |
156 |
|
char *cp = NULL; |
157 |
|
unsigned int buf_len = PAGE_SIZE / 2; |
158 |
struct dentry *dentry = path->dentry; |
struct dentry *dentry = path->dentry; |
159 |
if (!dentry || !newname || newname_len <= 2048) |
if (!dentry) |
160 |
goto out; |
return NULL; |
161 |
|
retry: |
162 |
|
buf_len <<= 1; |
163 |
|
kfree(buf); |
164 |
|
buf = kmalloc(buf_len, GFP_KERNEL); |
165 |
|
if (!buf) |
166 |
|
goto done; |
167 |
/* Get better name for socket. */ |
/* Get better name for socket. */ |
168 |
if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { |
if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { |
169 |
struct inode *inode = dentry->d_inode; |
struct inode *inode = dentry->d_inode; |
170 |
struct socket *sock = inode ? SOCKET_I(inode) : NULL; |
struct socket *sock = inode ? SOCKET_I(inode) : NULL; |
171 |
struct sock *sk = sock ? sock->sk : NULL; |
struct sock *sk = sock ? sock->sk : NULL; |
172 |
if (sk) { |
if (sk) { |
173 |
snprintf(newname, newname_len - 1, |
snprintf(buf, buf_len - 1, |
174 |
"socket:[family=%u:type=%u:protocol=%u]", |
"socket:[family=%u:type=%u:protocol=%u]", |
175 |
sk->sk_family, sk->sk_type, sk->sk_protocol); |
sk->sk_family, sk->sk_type, sk->sk_protocol); |
176 |
} else { |
} else { |
177 |
snprintf(newname, newname_len - 1, "socket:[unknown]"); |
snprintf(buf, buf_len - 1, "socket:[unknown]"); |
178 |
} |
} |
179 |
return 0; |
cp = ccs_encode(buf); |
180 |
|
goto done; |
181 |
} |
} |
182 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) |
183 |
|
/* For "socket:[\$]" and "pipe:[\$]". */ |
184 |
if (dentry->d_op && dentry->d_op->d_dname) { |
if (dentry->d_op && dentry->d_op->d_dname) { |
185 |
/* For "socket:[\$]" and "pipe:[\$]". */ |
cp = dentry->d_op->d_dname(dentry, buf, buf_len - 1); |
186 |
static const int offset = 1536; |
if (IS_ERR(cp)) |
187 |
char *dp = newname; |
goto retry; |
188 |
char *sp = dentry->d_op->d_dname(dentry, newname + offset, |
cp = ccs_encode(cp); |
189 |
newname_len - offset); |
goto done; |
|
if (IS_ERR(sp)) { |
|
|
error = PTR_ERR(sp); |
|
|
goto out; |
|
|
} |
|
|
error = -ENOMEM; |
|
|
newname += offset; |
|
|
while (1) { |
|
|
const unsigned char c = *(unsigned char *) sp++; |
|
|
if (c == '\\') { |
|
|
if (dp + 2 >= newname) |
|
|
break; |
|
|
*dp++ = '\\'; |
|
|
*dp++ = '\\'; |
|
|
} else if (c > ' ' && c < 127) { |
|
|
if (dp + 1 >= newname) |
|
|
break; |
|
|
*dp++ = (char) c; |
|
|
} else if (c) { |
|
|
if (dp + 4 >= newname) |
|
|
break; |
|
|
*dp++ = '\\'; |
|
|
*dp++ = (c >> 6) + '0'; |
|
|
*dp++ = ((c >> 3) & 7) + '0'; |
|
|
*dp++ = (c & 7) + '0'; |
|
|
} else { |
|
|
*dp = '\0'; |
|
|
return 0; |
|
|
} |
|
|
} |
|
|
goto out; |
|
190 |
} |
} |
191 |
#endif |
#endif |
192 |
if (!path->mnt) |
if (!path->mnt) { |
193 |
goto out; |
kfree(buf); |
194 |
|
return NULL; |
195 |
|
} |
196 |
path_get(path); |
path_get(path); |
197 |
ccs_realpath_lock(); |
ccs_realpath_lock(); |
198 |
error = ccs_get_absolute_path(path, newname, newname_len); |
cp = ccs_get_absolute_path(path, buf, buf_len - 1); |
199 |
ccs_realpath_unlock(); |
ccs_realpath_unlock(); |
200 |
path_put(path); |
path_put(path); |
201 |
out: |
if (IS_ERR(cp)) |
202 |
if (error) |
goto retry; |
203 |
printk(KERN_WARNING "ccs_realpath: Pathname too long. (%d)\n", |
cp = ccs_encode(cp); |
204 |
error); |
done: |
|
return error; |
|
|
} |
|
|
|
|
|
/** |
|
|
* ccs_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
|
|
* |
|
|
* @path: Pointer to "struct path". |
|
|
* |
|
|
* Returns the realpath of the given @path on success, NULL otherwise. |
|
|
* |
|
|
* These functions use kzalloc(), so caller must kfree() |
|
|
* if these functions didn't return NULL. |
|
|
*/ |
|
|
char *ccs_realpath_from_path(struct path *path) |
|
|
{ |
|
|
char *buf = kzalloc(CCS_MAX_PATHNAME_LEN, GFP_KERNEL); |
|
|
if (buf && |
|
|
ccs_realpath_from_path2(path, buf, CCS_MAX_PATHNAME_LEN - 2) == 0) |
|
|
return buf; |
|
205 |
kfree(buf); |
kfree(buf); |
206 |
return NULL; |
if (!cp) |
207 |
|
ccs_warn_oom(__func__); |
208 |
|
return cp; |
209 |
} |
} |
210 |
|
|
211 |
/** |
/** |
212 |
* ccs_symlink_path - Get symlink's pathname. |
* ccs_symlink_path - Get symlink's pathname. |
213 |
* |
* |
214 |
* @pathname: The pathname to solve. |
* @pathname: The pathname to solve. |
215 |
* @ee: Pointer to "struct ccs_execve_entry". |
* @name: Pointer to "struct ccs_path_info". |
216 |
* |
* |
217 |
* Returns 0 on success, negative value otherwise. |
* Returns 0 on success, negative value otherwise. |
218 |
|
* |
219 |
|
* This function uses kzalloc(), so caller must kfree() if this function |
220 |
|
* didn't return NULL. |
221 |
*/ |
*/ |
222 |
int ccs_symlink_path(const char *pathname, struct ccs_execve_entry *ee) |
int ccs_symlink_path(const char *pathname, struct ccs_path_info *name) |
223 |
{ |
{ |
224 |
|
char *buf; |
225 |
struct path path; |
struct path path; |
|
int ret; |
|
226 |
if (ccs_kern_path(pathname, ccs_lookup_flags ^ LOOKUP_FOLLOW, &path)) |
if (ccs_kern_path(pathname, ccs_lookup_flags ^ LOOKUP_FOLLOW, &path)) |
227 |
return -ENOENT; |
return -ENOENT; |
228 |
ret = ccs_realpath_from_path2(&path, ee->program_path, |
buf = ccs_realpath_from_path(&path); |
|
CCS_MAX_PATHNAME_LEN - 1); |
|
229 |
path_put(&path); |
path_put(&path); |
230 |
return ret; |
if (buf) { |
231 |
|
name->name = buf; |
232 |
|
ccs_fill_path_info(name); |
233 |
|
return 0; |
234 |
|
} |
235 |
|
return -ENOMEM; |
236 |
} |
} |
237 |
|
|
238 |
/** |
/** |
263 |
len += 4; |
len += 4; |
264 |
} |
} |
265 |
len++; |
len++; |
266 |
cp = kzalloc(len, GFP_KERNEL); |
/* Reserve space for appending "/". */ |
267 |
|
cp = kzalloc(len + 10, GFP_KERNEL); |
268 |
if (!cp) |
if (!cp) |
269 |
return NULL; |
return NULL; |
270 |
cp0 = cp; |
cp0 = cp; |