Open-Source-Software-Entwicklung und Downloads

Browse Subversion Repository

Contents of /trunk/1.6.x/ccs-patch/fs/realpath.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1135 - (show annotations) (download) (as text)
Thu Apr 24 07:34:11 2008 UTC (16 years ago) by kumaneko
File MIME type: text/x-csrc
File size: 15209 byte(s)


1 /*
2 * fs/realpath.c
3 *
4 * Get the canonicalized absolute pathnames. The basis for SAKURA and TOMOYO.
5 *
6 * Copyright (C) 2005-2008 NTT DATA CORPORATION
7 *
8 * Version: 1.6.1-rc 2008/04/24
9 *
10 * This file is applicable to both 2.4.30 and 2.6.11 and later.
11 * See README.ccs for ChangeLog.
12 *
13 */
14 #include <linux/string.h>
15 #include <linux/mm.h>
16 #include <linux/utime.h>
17 #include <linux/file.h>
18 #include <linux/smp_lock.h>
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <asm/uaccess.h>
22 #include <asm/atomic.h>
23 #include <linux/version.h>
24 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
25 #include <linux/namei.h>
26 #include <linux/mount.h>
27 static const int lookup_flags = LOOKUP_FOLLOW;
28 #else
29 static const int lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;
30 #endif
31 #include <linux/realpath.h>
32 #include <linux/proc_fs.h>
33 #include <linux/ccs_common.h>
34
35 /**
36 * get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
37 *
38 * @dentry: Pointer to "struct dentry".
39 * @vfsmnt: Pointer to "struct vfsmount".
40 * @buffer: Pointer to buffer to return value in.
41 * @buflen: Sizeof @buffer.
42 *
43 * Returns 0 on success, -ENOMEM otherwise.
44 *
45 * Caller holds the dcache_lock and vfsmount_lock.
46 * Based on __d_path() in fs/dcache.c
47 *
48 * If dentry is a directory, trailing '/' is appended.
49 * Characters out of 0x20 < c < 0x7F range are converted to
50 * \ooo style octal string.
51 * Character \ is converted to \\ string.
52 */
53 static int get_absolute_path(struct dentry *dentry, struct vfsmount *vfsmnt,
54 char *buffer, int buflen)
55 {
56 /***** CRITICAL SECTION START *****/
57 char *start = buffer;
58 char *end = buffer + buflen;
59 bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
60
61 if (buflen < 256)
62 goto out;
63
64 *--end = '\0';
65 buflen--;
66
67 for (;;) {
68 struct dentry *parent;
69
70 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
71 /* Global root? */
72 if (vfsmnt->mnt_parent == vfsmnt)
73 break;
74 dentry = vfsmnt->mnt_mountpoint;
75 vfsmnt = vfsmnt->mnt_parent;
76 continue;
77 }
78 if (is_dir) {
79 is_dir = false;
80 *--end = '/';
81 buflen--;
82 }
83 parent = dentry->d_parent;
84 {
85 const char *sp = dentry->d_name.name;
86 const char *cp = sp + dentry->d_name.len - 1;
87 unsigned char c;
88
89 /*
90 * Exception: Use /proc/self/ rather than
91 * /proc/\$/ for current process.
92 */
93 if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' &&
94 parent->d_sb &&
95 parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
96 char *ep;
97 const pid_t pid
98 = (pid_t) simple_strtoul(sp, &ep, 10);
99 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
100 if (!*ep && pid == current->tgid) {
101 sp = "self";
102 cp = sp + 3;
103 }
104 #else
105 if (!*ep && pid == current->pid) {
106 sp = "self";
107 cp = sp + 3;
108 }
109 #endif
110 }
111
112 while (sp <= cp) {
113 c = *(unsigned char *) cp;
114 if (c == '\\') {
115 buflen -= 2;
116 if (buflen < 0)
117 goto out;
118 *--end = '\\';
119 *--end = '\\';
120 } else if (c > ' ' && c < 127) {
121 if (--buflen < 0)
122 goto out;
123 *--end = (char) c;
124 } else {
125 buflen -= 4;
126 if (buflen < 0)
127 goto out;
128 *--end = (c & 7) + '0';
129 *--end = ((c >> 3) & 7) + '0';
130 *--end = (c >> 6) + '0';
131 *--end = '\\';
132 }
133 cp--;
134 }
135 if (--buflen < 0)
136 goto out;
137 *--end = '/';
138 }
139 dentry = parent;
140 }
141 if (*end == '/') {
142 buflen++;
143 end++;
144 }
145 {
146 const char *sp = dentry->d_name.name;
147 const char *cp = sp + dentry->d_name.len - 1;
148 unsigned char c;
149 while (sp <= cp) {
150 c = *(unsigned char *) cp;
151 if (c == '\\') {
152 buflen -= 2;
153 if (buflen < 0)
154 goto out;
155 *--end = '\\';
156 *--end = '\\';
157 } else if (c > ' ' && c < 127) {
158 if (--buflen < 0)
159 goto out;
160 *--end = (char) c;
161 } else {
162 buflen -= 4;
163 if (buflen < 0)
164 goto out;
165 *--end = (c & 7) + '0';
166 *--end = ((c >> 3) & 7) + '0';
167 *--end = (c >> 6) + '0';
168 *--end = '\\';
169 }
170 cp--;
171 }
172 }
173 /* Move the pathname to the top of the buffer. */
174 memmove(start, end, strlen(end) + 1);
175 return 0;
176 out:
177 return -ENOMEM;
178 /***** CRITICAL SECTION END *****/
179 }
180
181 /**
182 * ccs_realpath_from_dentry2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
183 *
184 * @dentry: Pointer to "struct dentry".
185 * @mnt: Pointer to "struct vfsmount".
186 * @newname: Pointer to buffer to return value in.
187 * @newname_len: Size of @newname.
188 *
189 * Returns 0 on success, negative value otherwise.
190 */
191 int ccs_realpath_from_dentry2(struct dentry *dentry, struct vfsmount *mnt,
192 char *newname, int newname_len)
193 {
194 int error;
195 struct dentry *d_dentry;
196 struct vfsmount *d_mnt;
197 if (!dentry || !mnt || !newname || newname_len <= 0)
198 return -EINVAL;
199 d_dentry = dget(dentry);
200 d_mnt = mntget(mnt);
201 /***** CRITICAL SECTION START *****/
202 spin_lock(&dcache_lock);
203 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
204 spin_lock(&vfsmount_lock);
205 #endif
206 error = get_absolute_path(d_dentry, d_mnt, newname, newname_len);
207 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
208 spin_unlock(&vfsmount_lock);
209 #endif
210 spin_unlock(&dcache_lock);
211 /***** CRITICAL SECTION END *****/
212 dput(d_dentry);
213 mntput(d_mnt);
214 if (error)
215 printk(KERN_WARNING "ccs_realpath: Pathname too long.\n");
216 return error;
217 }
218
219 /**
220 * ccs_realpath_from_dentry - Returns realpath(3) of the given pathname but ignores chroot'ed root.
221 *
222 * @dentry: Pointer to "struct dentry".
223 * @mnt: Pointer to "struct vfsmount".
224 *
225 * Returns the realpath of the given @dentry and @mnt on success,
226 * NULL otherwise.
227 *
228 * These functions use ccs_alloc(), so caller must ccs_free()
229 * if these functions didn't return NULL.
230 */
231 char *ccs_realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt)
232 {
233 char *buf = ccs_alloc(sizeof(struct ccs_page_buffer));
234 if (buf && ccs_realpath_from_dentry2(dentry, mnt, buf,
235 CCS_MAX_PATHNAME_LEN - 1) == 0)
236 return buf;
237 ccs_free(buf);
238 return NULL;
239 }
240
241 /**
242 * ccs_realpath - Get realpath of a pathname.
243 *
244 * @pathname: The pathname to solve.
245 *
246 * Returns the realpath of @pathname on success, NULL otherwise.
247 */
248 char *ccs_realpath(const char *pathname)
249 {
250 struct nameidata nd;
251 if (pathname && path_lookup(pathname, lookup_flags, &nd) == 0) {
252 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
253 char *buf = ccs_realpath_from_dentry(nd.path.dentry,
254 nd.path.mnt);
255 path_put(&nd.path);
256 #else
257 char *buf = ccs_realpath_from_dentry(nd.dentry, nd.mnt);
258 path_release(&nd);
259 #endif
260 return buf;
261 }
262 return NULL;
263 }
264
265 /**
266 * ccs_realpath_nofollow - Get realpath of a pathname.
267 *
268 * @pathname: The pathname to solve.
269 *
270 * Returns the realpath of @pathname on success, NULL otherwise.
271 */
272 char *ccs_realpath_nofollow(const char *pathname)
273 {
274 struct nameidata nd;
275 if (pathname && path_lookup(pathname, lookup_flags ^ LOOKUP_FOLLOW,
276 &nd) == 0) {
277 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
278 char *buf = ccs_realpath_from_dentry(nd.path.dentry,
279 nd.path.mnt);
280 path_put(&nd.path);
281 #else
282 char *buf = ccs_realpath_from_dentry(nd.dentry, nd.mnt);
283 path_release(&nd);
284 #endif
285 return buf;
286 }
287 return NULL;
288 }
289
290 /**
291 * round_up - Round up an integer so that the returned pointers are appropriately aligned.
292 *
293 * @size: Size in bytes.
294 *
295 * Returns rounded value of @size.
296 *
297 * FIXME: Are there more requirements that is needed for assigning value
298 * atomically?
299 */
300 static inline unsigned int round_up(const unsigned int size)
301 {
302 if (sizeof(void *) >= sizeof(long))
303 return ((size + sizeof(void *) - 1)
304 / sizeof(void *)) * sizeof(void *);
305 else
306 return ((size + sizeof(long) - 1)
307 / sizeof(long)) * sizeof(long);
308 }
309
310 static unsigned int allocated_memory_for_elements;
311
312 /**
313 * ccs_get_memory_used_for_elements - Get memory used for keeping ACL structures.
314 *
315 * Returns memory used for keeping ACL structures.
316 */
317 unsigned int ccs_get_memory_used_for_elements(void)
318 {
319 return allocated_memory_for_elements;
320 }
321
322 /**
323 * ccs_alloc_element - Allocate permanent memory for structures.
324 *
325 * @size: Size in bytes.
326 *
327 * Returns pointer to allocated memory on success, NULL otherwise.
328 *
329 * The RAM is chunked, so NEVER try to kfree() the returned pointer.
330 */
331 void *ccs_alloc_element(const unsigned int size)
332 {
333 static DEFINE_MUTEX(lock);
334 static char *buf;
335 static unsigned int buf_used_len = PAGE_SIZE;
336 char *ptr = NULL;
337 const unsigned int word_aligned_size = round_up(size);
338 if (word_aligned_size > PAGE_SIZE)
339 return NULL;
340 mutex_lock(&lock);
341 if (buf_used_len + word_aligned_size > PAGE_SIZE) {
342 ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
343 if (!ptr) {
344 printk(KERN_WARNING "ERROR: Out of memory "
345 "for ccs_alloc_element().\n");
346 if (!sbin_init_started)
347 panic("MAC Initialization failed.\n");
348 } else {
349 buf = ptr;
350 allocated_memory_for_elements += PAGE_SIZE;
351 buf_used_len = word_aligned_size;
352 ptr = buf;
353 }
354 } else if (word_aligned_size) {
355 int i;
356 ptr = buf + buf_used_len;
357 buf_used_len += word_aligned_size;
358 for (i = 0; i < word_aligned_size; i++) {
359 if (!ptr[i])
360 continue;
361 printk(KERN_ERR "WARNING: Reserved memory was tainted! "
362 "The system might go wrong.\n");
363 ptr[i] = '\0';
364 }
365 }
366 mutex_unlock(&lock);
367 return ptr;
368 }
369
370 static unsigned int allocated_memory_for_savename;
371
372 /**
373 * ccs_get_memory_used_for_save_name - Get memory used for keeping string data.
374 *
375 * Returns memory used for keeping string data.
376 */
377 unsigned int ccs_get_memory_used_for_save_name(void)
378 {
379 return allocated_memory_for_savename;
380 }
381
382 #define MAX_HASH 256
383
384 /* Structure for string data. */
385 struct name_entry {
386 struct list1_head list;
387 struct path_info entry;
388 };
389
390 /* Structure for available memory region. */
391 struct free_memory_block_list {
392 struct list_head list;
393 char *ptr; /* Pointer to a free area. */
394 int len; /* Length of the area. */
395 };
396
397 /* The list for "struct name_entry". */
398 static struct list1_head name_list[MAX_HASH];
399
400 /**
401 * ccs_save_name - Allocate permanent memory for string data.
402 *
403 * @name: The string to store into the permernent memory.
404 *
405 * Returns pointer to "struct path_info" on success, NULL otherwise.
406 *
407 * The RAM is shared, so NEVER try to modify or kfree() the returned name.
408 */
409 const struct path_info *ccs_save_name(const char *name)
410 {
411 static LIST_HEAD(fmb_list);
412 static DEFINE_MUTEX(lock);
413 struct name_entry *ptr;
414 unsigned int hash;
415 struct free_memory_block_list *fmb;
416 int len;
417 char *cp;
418 if (!name)
419 return NULL;
420 len = strlen(name) + 1;
421 if (len > CCS_MAX_PATHNAME_LEN) {
422 printk(KERN_WARNING "ERROR: Name too long "
423 "for ccs_save_name().\n");
424 return NULL;
425 }
426 hash = full_name_hash((const unsigned char *) name, len - 1);
427 mutex_lock(&lock);
428 list1_for_each_entry(ptr, &name_list[hash % MAX_HASH], list) {
429 if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
430 goto out;
431 }
432 list_for_each_entry(fmb, &fmb_list, list) {
433 if (len <= fmb->len)
434 goto ready;
435 }
436 cp = kzalloc(PAGE_SIZE, GFP_KERNEL);
437 fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
438 if (!cp || !fmb) {
439 kfree(cp);
440 kfree(fmb);
441 printk(KERN_WARNING "ERROR: Out of memory "
442 "for ccs_save_name().\n");
443 if (!sbin_init_started)
444 panic("MAC Initialization failed.\n");
445 ptr = NULL;
446 goto out;
447 }
448 allocated_memory_for_savename += PAGE_SIZE;
449 list_add(&fmb->list, &fmb_list);
450 fmb->ptr = cp;
451 fmb->len = PAGE_SIZE;
452 ready:
453 ptr = ccs_alloc_element(sizeof(*ptr));
454 if (!ptr)
455 goto out;
456 ptr->entry.name = fmb->ptr;
457 memmove(fmb->ptr, name, len);
458 ccs_fill_path_info(&ptr->entry);
459 fmb->ptr += len;
460 fmb->len -= len;
461 list1_add_tail_mb(&ptr->list, &name_list[hash % MAX_HASH]);
462 if (fmb->len == 0) {
463 list_del(&fmb->list);
464 kfree(fmb);
465 }
466 out:
467 mutex_unlock(&lock);
468 return ptr ? &ptr->entry : NULL;
469 }
470
471 /* Structure for temporarily allocated memory. */
472 struct cache_entry {
473 struct list_head list;
474 void *ptr;
475 int size;
476 };
477
478 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
479 static struct kmem_cache *ccs_cachep;
480 #else
481 static kmem_cache_t *ccs_cachep;
482 #endif
483
484 /**
485 * ccs_realpath_init - Initialize realpath related code.
486 *
487 * Returns 0.
488 */
489 static int __init ccs_realpath_init(void)
490 {
491 int i;
492 if (CCS_MAX_PATHNAME_LEN > PAGE_SIZE)
493 panic("Bad size.");
494 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
495 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry),
496 0, 0, NULL);
497 #else
498 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry),
499 0, 0, NULL, NULL);
500 #endif
501 if (!ccs_cachep)
502 panic("Can't create cache.\n");
503 for (i = 0; i < MAX_HASH; i++)
504 INIT_LIST1_HEAD(&name_list[i]);
505 INIT_LIST1_HEAD(&KERNEL_DOMAIN.acl_info_list);
506 KERNEL_DOMAIN.domainname = ccs_save_name(ROOT_NAME);
507 list1_add_tail_mb(&KERNEL_DOMAIN.list, &domain_list);
508 if (ccs_find_domain(ROOT_NAME) != &KERNEL_DOMAIN)
509 panic("Can't register KERNEL_DOMAIN");
510 return 0;
511 }
512
513 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
514 __initcall(ccs_realpath_init);
515 #else
516 core_initcall(ccs_realpath_init);
517 #endif
518
519 /* The list for "struct cache_entry". */
520 static LIST_HEAD(cache_list);
521
522 static DEFINE_SPINLOCK(cache_list_lock);
523
524 static unsigned int dynamic_memory_size;
525
526 /**
527 * ccs_get_memory_used_for_dynamic - Get memory used for temporal purpose.
528 *
529 * Returns memory used for temporal purpose.
530 */
531 unsigned int ccs_get_memory_used_for_dynamic(void)
532 {
533 return dynamic_memory_size;
534 }
535
536 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
537 /**
538 * round2 - Rounded up to power-of-two value.
539 *
540 * @size: Size in bytes.
541 *
542 * Returns power-of-two of @size.
543 */
544 static int round2(size_t size)
545 {
546 #if PAGE_SIZE == 4096
547 size_t bsize = 32;
548 #else
549 size_t bsize = 64;
550 #endif
551 while (size > bsize)
552 bsize <<= 1;
553 return bsize;
554 }
555 #endif
556
557 /**
558 * ccs_alloc - Allocate memory for temporal purpose.
559 *
560 * @size: Size in bytes.
561 *
562 * Returns pointer to allocated memory on success, NULL otherwise.
563 */
564 void *ccs_alloc(const size_t size)
565 {
566 struct cache_entry *new_entry;
567 void *ret = kzalloc(size, GFP_KERNEL);
568 if (!ret)
569 goto out;
570 new_entry = kmem_cache_alloc(ccs_cachep, GFP_KERNEL);
571 if (!new_entry) {
572 kfree(ret);
573 ret = NULL;
574 goto out;
575 }
576 INIT_LIST_HEAD(&new_entry->list);
577 new_entry->ptr = ret;
578 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
579 new_entry->size = ksize(ret);
580 #else
581 new_entry->size = round2(size);
582 #endif
583 /***** CRITICAL SECTION START *****/
584 spin_lock(&cache_list_lock);
585 list_add_tail(&new_entry->list, &cache_list);
586 dynamic_memory_size += new_entry->size;
587 spin_unlock(&cache_list_lock);
588 /***** CRITICAL SECTION END *****/
589 out:
590 return ret;
591 }
592
593 /**
594 * ccs_free - Release memory allocated by ccs_alloc().
595 *
596 * @p: Pointer returned by ccs_alloc(). May be NULL.
597 *
598 * Returns nothing.
599 */
600 void ccs_free(const void *p)
601 {
602 struct list_head *v;
603 struct cache_entry *entry = NULL;
604 if (!p)
605 return;
606 /***** CRITICAL SECTION START *****/
607 spin_lock(&cache_list_lock);
608 list_for_each(v, &cache_list) {
609 entry = list_entry(v, struct cache_entry, list);
610 if (entry->ptr != p) {
611 entry = NULL;
612 continue;
613 }
614 list_del(&entry->list);
615 dynamic_memory_size -= entry->size;
616 break;
617 }
618 spin_unlock(&cache_list_lock);
619 /***** CRITICAL SECTION END *****/
620 if (entry) {
621 kfree(p);
622 kmem_cache_free(ccs_cachep, entry);
623 } else {
624 printk(KERN_WARNING "BUG: ccs_free() with invalid pointer.\n");
625 }
626 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26