1 |
/* |
2 |
* security/ccsecurity/realpath.c |
3 |
* |
4 |
* Copyright (C) 2005-2011 NTT DATA CORPORATION |
5 |
* |
6 |
* Version: 1.8.0+ 2011/01/11 |
7 |
*/ |
8 |
|
9 |
#include "internal.h" |
10 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
11 |
#define ccs_lookup_flags LOOKUP_FOLLOW |
12 |
#else |
13 |
#define ccs_lookup_flags (LOOKUP_FOLLOW | LOOKUP_POSITIVE) |
14 |
#endif |
15 |
|
16 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
17 |
#define s_fs_info u.generic_sbp |
18 |
#endif |
19 |
|
20 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
21 |
|
22 |
/** |
23 |
* SOCKET_I - Get "struct socket". |
24 |
* |
25 |
* @inode: Pointer to "struct inode". |
26 |
* |
27 |
* Returns pointer to "struct socket". |
28 |
* |
29 |
* This is for compatibility with older kernels. |
30 |
*/ |
31 |
static inline struct socket *SOCKET_I(struct inode *inode) |
32 |
{ |
33 |
return inode->i_sock ? &inode->u.socket_i : NULL; |
34 |
} |
35 |
|
36 |
#endif |
37 |
|
38 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) |
39 |
|
40 |
/** |
41 |
* ccs_realpath_lock - Take locks for __d_path(). |
42 |
* |
43 |
* Returns nothing. |
44 |
*/ |
45 |
static inline void ccs_realpath_lock(void) |
46 |
{ |
47 |
/* dcache_lock is locked by __d_path(). */ |
48 |
/* vfsmount_lock is locked by __d_path(). */ |
49 |
} |
50 |
|
51 |
/** |
52 |
* ccs_realpath_unlock - Release locks for __d_path(). |
53 |
* |
54 |
* Returns nothing. |
55 |
*/ |
56 |
static inline void ccs_realpath_unlock(void) |
57 |
{ |
58 |
/* vfsmount_lock is unlocked by __d_path(). */ |
59 |
/* dcache_lock is unlocked by __d_path(). */ |
60 |
} |
61 |
|
62 |
#elif LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36) |
63 |
|
64 |
/** |
65 |
* ccs_realpath_lock - Take locks for __d_path(). |
66 |
* |
67 |
* Returns nothing. |
68 |
*/ |
69 |
static inline void ccs_realpath_lock(void) |
70 |
{ |
71 |
spin_lock(&dcache_lock); |
72 |
/* vfsmount_lock is locked by __d_path(). */ |
73 |
} |
74 |
|
75 |
/** |
76 |
* ccs_realpath_unlock - Release locks for __d_path(). |
77 |
* |
78 |
* Returns nothing. |
79 |
*/ |
80 |
static inline void ccs_realpath_unlock(void) |
81 |
{ |
82 |
/* vfsmount_lock is unlocked by __d_path(). */ |
83 |
spin_unlock(&dcache_lock); |
84 |
} |
85 |
|
86 |
#elif defined(D_PATH_DISCONNECT) && !defined(CONFIG_SUSE_KERNEL) |
87 |
|
88 |
/** |
89 |
* ccs_realpath_lock - Take locks for __d_path(). |
90 |
* |
91 |
* Returns nothing. |
92 |
* |
93 |
* Original unambiguous-__d_path.diff in patches.apparmor.tar.bz2 inversed the |
94 |
* order of holding dcache_lock and vfsmount_lock. That patch was applied on |
95 |
* (at least) SUSE 11.1 and Ubuntu 8.10 and Ubuntu 9.04 kernels. |
96 |
* |
97 |
* However, that patch was updated to use original order and the updated patch |
98 |
* is applied to (as far as I know) only SUSE kernels. |
99 |
* |
100 |
* Therefore, I need to use original order for SUSE 11.1 kernels and inversed |
101 |
* order for other kernels. I detect it by checking D_PATH_DISCONNECT and |
102 |
* CONFIG_SUSE_KERNEL. I don't know whether other distributions are using the |
103 |
* updated patch or not. If you got deadlock, check fs/dcache.c for locking |
104 |
* order, and add " && 0" to this "#elif " block if fs/dcache.c uses original |
105 |
* order. |
106 |
*/ |
107 |
static inline void ccs_realpath_lock(void) |
108 |
{ |
109 |
spin_lock(ccsecurity_exports.vfsmount_lock); |
110 |
spin_lock(&dcache_lock); |
111 |
} |
112 |
|
113 |
/** |
114 |
* ccs_realpath_unlock - Release locks for __d_path(). |
115 |
* |
116 |
* Returns nothing. |
117 |
*/ |
118 |
static inline void ccs_realpath_unlock(void) |
119 |
{ |
120 |
spin_unlock(&dcache_lock); |
121 |
spin_unlock(ccsecurity_exports.vfsmount_lock); |
122 |
} |
123 |
|
124 |
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
125 |
|
126 |
/** |
127 |
* ccs_realpath_lock - Take locks for __d_path(). |
128 |
* |
129 |
* Returns nothing. |
130 |
*/ |
131 |
static inline void ccs_realpath_lock(void) |
132 |
{ |
133 |
spin_lock(&dcache_lock); |
134 |
spin_lock(ccsecurity_exports.vfsmount_lock); |
135 |
} |
136 |
|
137 |
/** |
138 |
* ccs_realpath_unlock - Release locks for __d_path(). |
139 |
* |
140 |
* Returns nothing. |
141 |
*/ |
142 |
static inline void ccs_realpath_unlock(void) |
143 |
{ |
144 |
spin_unlock(ccsecurity_exports.vfsmount_lock); |
145 |
spin_unlock(&dcache_lock); |
146 |
} |
147 |
|
148 |
#else |
149 |
|
150 |
/** |
151 |
* ccs_realpath_lock - Take locks for __d_path(). |
152 |
* |
153 |
* Returns nothing. |
154 |
*/ |
155 |
static inline void ccs_realpath_lock(void) |
156 |
{ |
157 |
spin_lock(&dcache_lock); |
158 |
} |
159 |
|
160 |
/** |
161 |
* ccs_realpath_unlock - Release locks for __d_path(). |
162 |
* |
163 |
* Returns nothing. |
164 |
*/ |
165 |
static inline void ccs_realpath_unlock(void) |
166 |
{ |
167 |
spin_unlock(&dcache_lock); |
168 |
} |
169 |
|
170 |
#endif |
171 |
|
172 |
/** |
173 |
* ccs_kern_path - Wrapper for kern_path(). |
174 |
* |
175 |
* @pathname: Pathname to resolve. Maybe NULL. |
176 |
* @flags: Lookup flags. |
177 |
* @path: Pointer to "struct path". |
178 |
* |
179 |
* Returns 0 on success, negative value otherwise. |
180 |
*/ |
181 |
static int ccs_kern_path(const char *pathname, int flags, struct path *path) |
182 |
{ |
183 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28) |
184 |
if (!pathname || kern_path(pathname, flags, path)) |
185 |
return -ENOENT; |
186 |
#else |
187 |
struct nameidata nd; |
188 |
if (!pathname || path_lookup(pathname, flags, &nd)) |
189 |
return -ENOENT; |
190 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) |
191 |
*path = nd.path; |
192 |
#else |
193 |
path->dentry = nd.dentry; |
194 |
path->mnt = nd.mnt; |
195 |
#endif |
196 |
#endif |
197 |
return 0; |
198 |
} |
199 |
|
200 |
/** |
201 |
* ccs_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. |
202 |
* |
203 |
* @path: Pointer to "struct path". |
204 |
* @buffer: Pointer to buffer to return value in. |
205 |
* @buflen: Sizeof @buffer. |
206 |
* |
207 |
* Returns the buffer on success, an error code otherwise. |
208 |
* |
209 |
* Caller holds the dcache_lock and vfsmount_lock. |
210 |
* Based on __d_path() in fs/dcache.c |
211 |
* |
212 |
* If dentry is a directory, trailing '/' is appended. |
213 |
*/ |
214 |
static char *ccs_get_absolute_path(struct path *path, char * const buffer, |
215 |
const int buflen) |
216 |
{ |
217 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) |
218 |
char *pos = ERR_PTR(-ENOMEM); |
219 |
if (buflen >= 256) { |
220 |
struct path root = { }; |
221 |
pos = ccsecurity_exports.__d_path(path, &root, buffer, |
222 |
buflen - 1); |
223 |
if (!IS_ERR(pos) && *pos == '/' && pos[1]) { |
224 |
struct inode *inode = path->dentry->d_inode; |
225 |
if (inode && S_ISDIR(inode->i_mode)) { |
226 |
buffer[buflen - 2] = '/'; |
227 |
buffer[buflen - 1] = '\0'; |
228 |
} |
229 |
} |
230 |
} |
231 |
return pos; |
232 |
#else |
233 |
char *pos = buffer + buflen - 1; |
234 |
struct dentry *dentry = path->dentry; |
235 |
struct vfsmount *vfsmnt = path->mnt; |
236 |
const char *name; |
237 |
int len; |
238 |
|
239 |
if (buflen < 256) |
240 |
goto out; |
241 |
|
242 |
*pos = '\0'; |
243 |
if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) |
244 |
*--pos = '/'; |
245 |
for (;;) { |
246 |
struct dentry *parent; |
247 |
if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { |
248 |
if (vfsmnt->mnt_parent == vfsmnt) |
249 |
break; |
250 |
dentry = vfsmnt->mnt_mountpoint; |
251 |
vfsmnt = vfsmnt->mnt_parent; |
252 |
continue; |
253 |
} |
254 |
parent = dentry->d_parent; |
255 |
name = dentry->d_name.name; |
256 |
len = dentry->d_name.len; |
257 |
pos -= len; |
258 |
if (pos <= buffer) |
259 |
goto out; |
260 |
memmove(pos, name, len); |
261 |
*--pos = '/'; |
262 |
dentry = parent; |
263 |
} |
264 |
if (*pos == '/') |
265 |
pos++; |
266 |
len = dentry->d_name.len; |
267 |
pos -= len; |
268 |
if (pos < buffer) |
269 |
goto out; |
270 |
memmove(pos, dentry->d_name.name, len); |
271 |
return pos; |
272 |
out: |
273 |
return ERR_PTR(-ENOMEM); |
274 |
#endif |
275 |
} |
276 |
|
277 |
/** |
278 |
* ccs_get_dentry_path - Get the path of a dentry. |
279 |
* |
280 |
* @dentry: Pointer to "struct dentry". |
281 |
* @buffer: Pointer to buffer to return value in. |
282 |
* @buflen: Sizeof @buffer. |
283 |
* |
284 |
* Returns the buffer on success, an error code otherwise. |
285 |
* |
286 |
* Caller holds the dcache_lock. |
287 |
* Based on dentry_path() in fs/dcache.c |
288 |
* |
289 |
* If dentry is a directory, trailing '/' is appended. |
290 |
*/ |
291 |
static char *ccs_get_dentry_path(struct dentry *dentry, char * const buffer, |
292 |
const int buflen) |
293 |
{ |
294 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38) |
295 |
char *pos = ERR_PTR(-ENOMEM); |
296 |
if (buflen >= 256) { |
297 |
/* rename_lock is locked/unlocked by dentry_path_raw(). */ |
298 |
pos = dentry_path_raw(dentry, buffer, buflen - 1); |
299 |
if (!IS_ERR(pos) && *pos == '/' && pos[1]) { |
300 |
struct inode *inode = dentry->d_inode; |
301 |
if (inode && S_ISDIR(inode->i_mode)) { |
302 |
buffer[buflen - 2] = '/'; |
303 |
buffer[buflen - 1] = '\0'; |
304 |
} |
305 |
} |
306 |
} |
307 |
return pos; |
308 |
#else |
309 |
char *pos = buffer + buflen - 1; |
310 |
if (buflen < 256) |
311 |
goto out; |
312 |
*pos = '\0'; |
313 |
if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) |
314 |
*--pos = '/'; |
315 |
while (!IS_ROOT(dentry)) { |
316 |
struct dentry *parent = dentry->d_parent; |
317 |
const char *name = dentry->d_name.name; |
318 |
const int len = dentry->d_name.len; |
319 |
pos -= len; |
320 |
if (pos <= buffer) |
321 |
goto out; |
322 |
memmove(pos, name, len); |
323 |
*--pos = '/'; |
324 |
dentry = parent; |
325 |
} |
326 |
return pos; |
327 |
out: |
328 |
return ERR_PTR(-ENOMEM); |
329 |
#endif |
330 |
} |
331 |
|
332 |
/** |
333 |
* ccs_get_local_path - Get the path of a dentry. |
334 |
* |
335 |
* @dentry: Pointer to "struct dentry". |
336 |
* @buffer: Pointer to buffer to return value in. |
337 |
* @buflen: Sizeof @buffer. |
338 |
* |
339 |
* Returns the buffer on success, an error code otherwise. |
340 |
*/ |
341 |
static char *ccs_get_local_path(struct dentry *dentry, char * const buffer, |
342 |
const int buflen) |
343 |
{ |
344 |
char *pos; |
345 |
struct super_block *sb = dentry->d_sb; |
346 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) |
347 |
spin_lock(&dcache_lock); |
348 |
#endif |
349 |
pos = ccs_get_dentry_path(dentry, buffer, buflen); |
350 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) |
351 |
spin_unlock(&dcache_lock); |
352 |
#endif |
353 |
if (IS_ERR(pos)) |
354 |
return pos; |
355 |
/* Convert from $PID to self if $PID is current thread. */ |
356 |
if (sb->s_magic == PROC_SUPER_MAGIC && *pos == '/') { |
357 |
char *ep; |
358 |
const pid_t pid = (pid_t) simple_strtoul(pos + 1, &ep, 10); |
359 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) |
360 |
if (*ep == '/' && pid && pid == |
361 |
task_tgid_nr_ns(current, sb->s_fs_info)) { |
362 |
pos = ep - 5; |
363 |
if (pos < buffer) |
364 |
goto out; |
365 |
memmove(pos, "/self", 5); |
366 |
} |
367 |
#else |
368 |
if (*ep == '/' && pid == ccs_sys_getpid()) { |
369 |
pos = ep - 5; |
370 |
if (pos < buffer) |
371 |
goto out; |
372 |
memmove(pos, "/self", 5); |
373 |
} |
374 |
#endif |
375 |
goto prepend_filesystem_name; |
376 |
} |
377 |
/* Use filesystem name for unnamed devices. */ |
378 |
if (!MAJOR(sb->s_dev)) |
379 |
goto prepend_filesystem_name; |
380 |
{ |
381 |
struct inode *inode = sb->s_root->d_inode; |
382 |
/* |
383 |
* Use filesystem name if filesystems does not support rename() |
384 |
* operation. |
385 |
*/ |
386 |
if (inode->i_op && !inode->i_op->rename) |
387 |
goto prepend_filesystem_name; |
388 |
} |
389 |
/* Prepend device name. */ |
390 |
{ |
391 |
char name[64]; |
392 |
int name_len; |
393 |
const dev_t dev = sb->s_dev; |
394 |
name[sizeof(name) - 1] = '\0'; |
395 |
snprintf(name, sizeof(name) - 1, "dev(%u,%u):", MAJOR(dev), |
396 |
MINOR(dev)); |
397 |
name_len = strlen(name); |
398 |
pos -= name_len; |
399 |
if (pos < buffer) |
400 |
goto out; |
401 |
memmove(pos, name, name_len); |
402 |
return pos; |
403 |
} |
404 |
/* Prepend filesystem name. */ |
405 |
prepend_filesystem_name: |
406 |
{ |
407 |
const char *name = sb->s_type->name; |
408 |
const int name_len = strlen(name); |
409 |
pos -= name_len + 1; |
410 |
if (pos < buffer) |
411 |
goto out; |
412 |
memmove(pos, name, name_len); |
413 |
pos[name_len] = ':'; |
414 |
} |
415 |
return pos; |
416 |
out: |
417 |
return ERR_PTR(-ENOMEM); |
418 |
} |
419 |
|
420 |
/** |
421 |
* ccs_get_socket_name - Get the name of a socket. |
422 |
* |
423 |
* @path: Pointer to "struct path". |
424 |
* @buffer: Pointer to buffer to return value in. |
425 |
* @buflen: Sizeof @buffer. |
426 |
* |
427 |
* Returns the buffer. |
428 |
*/ |
429 |
static char *ccs_get_socket_name(struct path *path, char * const buffer, |
430 |
const int buflen) |
431 |
{ |
432 |
struct inode *inode = path->dentry->d_inode; |
433 |
struct socket *sock = inode ? SOCKET_I(inode) : NULL; |
434 |
struct sock *sk = sock ? sock->sk : NULL; |
435 |
if (sk) { |
436 |
snprintf(buffer, buflen, "socket:[family=%u:type=%u:" |
437 |
"protocol=%u]", sk->sk_family, sk->sk_type, |
438 |
sk->sk_protocol); |
439 |
} else { |
440 |
snprintf(buffer, buflen, "socket:[unknown]"); |
441 |
} |
442 |
return buffer; |
443 |
} |
444 |
|
445 |
#define SOCKFS_MAGIC 0x534F434B |
446 |
|
447 |
/** |
448 |
* ccs_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. |
449 |
* |
450 |
* @path: Pointer to "struct path". |
451 |
* |
452 |
* Returns the realpath of the given @path on success, NULL otherwise. |
453 |
* |
454 |
* This function uses kzalloc(), so caller must kfree() if this function |
455 |
* didn't return NULL. |
456 |
*/ |
457 |
char *ccs_realpath_from_path(struct path *path) |
458 |
{ |
459 |
char *buf = NULL; |
460 |
char *name = NULL; |
461 |
unsigned int buf_len = PAGE_SIZE / 2; |
462 |
struct dentry *dentry = path->dentry; |
463 |
struct super_block *sb; |
464 |
if (!dentry) |
465 |
return NULL; |
466 |
sb = dentry->d_sb; |
467 |
while (1) { |
468 |
char *pos; |
469 |
struct inode *inode; |
470 |
buf_len <<= 1; |
471 |
kfree(buf); |
472 |
buf = kmalloc(buf_len, CCS_GFP_FLAGS); |
473 |
if (!buf) |
474 |
break; |
475 |
/* To make sure that pos is '\0' terminated. */ |
476 |
buf[buf_len - 1] = '\0'; |
477 |
/* Get better name for socket. */ |
478 |
if (sb->s_magic == SOCKFS_MAGIC) { |
479 |
pos = ccs_get_socket_name(path, buf, buf_len - 1); |
480 |
goto encode; |
481 |
} |
482 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) |
483 |
/* For "pipe:[\$]". */ |
484 |
if (dentry->d_op && dentry->d_op->d_dname) { |
485 |
pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); |
486 |
goto encode; |
487 |
} |
488 |
#endif |
489 |
inode = sb->s_root->d_inode; |
490 |
/* |
491 |
* Get local name for filesystems without rename() operation |
492 |
* or dentry without vfsmount. |
493 |
*/ |
494 |
if (!path->mnt || (inode->i_op && !inode->i_op->rename)) { |
495 |
pos = ccs_get_local_path(path->dentry, buf, |
496 |
buf_len - 1); |
497 |
goto encode; |
498 |
} |
499 |
/* Get absolute name for the rest. */ |
500 |
ccs_realpath_lock(); |
501 |
pos = ccs_get_absolute_path(path, buf, buf_len - 1); |
502 |
ccs_realpath_unlock(); |
503 |
encode: |
504 |
if (IS_ERR(pos)) |
505 |
continue; |
506 |
name = ccs_encode(pos); |
507 |
break; |
508 |
} |
509 |
kfree(buf); |
510 |
if (!name) |
511 |
ccs_warn_oom(__func__); |
512 |
return name; |
513 |
} |
514 |
|
515 |
/** |
516 |
* ccs_symlink_path - Get symlink's pathname. |
517 |
* |
518 |
* @pathname: The pathname to solve. |
519 |
* @name: Pointer to "struct ccs_path_info". |
520 |
* |
521 |
* Returns 0 on success, negative value otherwise. |
522 |
* |
523 |
* This function uses kzalloc(), so caller must kfree() if this function |
524 |
* didn't return NULL. |
525 |
*/ |
526 |
int ccs_symlink_path(const char *pathname, struct ccs_path_info *name) |
527 |
{ |
528 |
char *buf; |
529 |
struct path path; |
530 |
if (ccs_kern_path(pathname, ccs_lookup_flags ^ LOOKUP_FOLLOW, &path)) |
531 |
return -ENOENT; |
532 |
buf = ccs_realpath_from_path(&path); |
533 |
path_put(&path); |
534 |
if (buf) { |
535 |
name->name = buf; |
536 |
ccs_fill_path_info(name); |
537 |
return 0; |
538 |
} |
539 |
return -ENOMEM; |
540 |
} |
541 |
|
542 |
/** |
543 |
* ccs_encode2 - Encode binary string to ascii string. |
544 |
* |
545 |
* @str: String in binary format. |
546 |
* @str_len: Size of @str in byte. |
547 |
* |
548 |
* Returns pointer to @str in ascii format on success, NULL otherwise. |
549 |
* |
550 |
* This function uses kzalloc(), so caller must kfree() if this function |
551 |
* didn't return NULL. |
552 |
*/ |
553 |
char *ccs_encode2(const char *str, int str_len) |
554 |
{ |
555 |
int i; |
556 |
int len = 0; |
557 |
const char *p = str; |
558 |
char *cp; |
559 |
char *cp0; |
560 |
if (!p) |
561 |
return NULL; |
562 |
for (i = 0; i < str_len; i++) { |
563 |
const unsigned char c = p[i]; |
564 |
if (c == '\\') |
565 |
len += 2; |
566 |
else if (c > ' ' && c < 127) |
567 |
len++; |
568 |
else |
569 |
len += 4; |
570 |
} |
571 |
len++; |
572 |
/* Reserve space for appending "/". */ |
573 |
cp = kzalloc(len + 10, CCS_GFP_FLAGS); |
574 |
if (!cp) |
575 |
return NULL; |
576 |
cp0 = cp; |
577 |
p = str; |
578 |
for (i = 0; i < str_len; i++) { |
579 |
const unsigned char c = p[i]; |
580 |
if (c == '\\') { |
581 |
*cp++ = '\\'; |
582 |
*cp++ = '\\'; |
583 |
} else if (c > ' ' && c < 127) { |
584 |
*cp++ = c; |
585 |
} else { |
586 |
*cp++ = '\\'; |
587 |
*cp++ = (c >> 6) + '0'; |
588 |
*cp++ = ((c >> 3) & 7) + '0'; |
589 |
*cp++ = (c & 7) + '0'; |
590 |
} |
591 |
} |
592 |
return cp0; |
593 |
} |
594 |
|
595 |
/** |
596 |
* ccs_encode - Encode binary string to ascii string. |
597 |
* |
598 |
* @str: String in binary format. |
599 |
* |
600 |
* Returns pointer to @str in ascii format on success, NULL otherwise. |
601 |
* |
602 |
* This function uses kzalloc(), so caller must kfree() if this function |
603 |
* didn't return NULL. |
604 |
*/ |
605 |
char *ccs_encode(const char *str) |
606 |
{ |
607 |
return str ? ccs_encode2(str, strlen(str)) : NULL; |
608 |
} |
609 |
|
610 |
/** |
611 |
* ccs_get_path - Get dentry/vfsmmount of a pathname. |
612 |
* |
613 |
* @pathname: The pathname to solve. |
614 |
* @path: Pointer to "struct path". |
615 |
* |
616 |
* Returns 0 on success, negative value otherwise. |
617 |
*/ |
618 |
int ccs_get_path(const char *pathname, struct path *path) |
619 |
{ |
620 |
return ccs_kern_path(pathname, ccs_lookup_flags, path); |
621 |
} |