1 |
/* |
2 |
* include/linux/syaoran.h |
3 |
* |
4 |
* Implementation of the Tamper-Proof Device Filesystem. |
5 |
* |
6 |
* Copyright (C) 2005-2008 NTT DATA CORPORATION |
7 |
* |
8 |
* Version: 1.6.6-pre 2008/12/01 |
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 |
/* |
15 |
* A brief description about SYAORAN: |
16 |
* |
17 |
* SYAORAN stands for "Simple Yet All-important Object Realizing Abiding |
18 |
* Nexus". SYAORAN is a filesystem for /dev with Mandatory Access Control. |
19 |
* |
20 |
* /dev cannot be mounted for read-only mode, but this means that files on |
21 |
* /dev might be tampered with. In other words, a device file might have |
22 |
* inappropriate attributes (e.g. /dev/null has char-1-5 attributes). |
23 |
* SYAORAN can restrict combinations of (pathname, attribute) that |
24 |
* the system can create so that all files on this filesystem have appropriate |
25 |
* attributes (e.g. /dev/null has char-1-3 attributes). |
26 |
* |
27 |
* The attribute is one of directory, regular file, FIFO, UNIX domain socket, |
28 |
* symbolic link, character or block device file with major/minor device |
29 |
* numbers. |
30 |
* |
31 |
* You can use SYAORAN alone, but I recommend you to use SYAORAN |
32 |
* with SAKURA and TOMOYO. |
33 |
*/ |
34 |
|
35 |
#ifndef _LINUX_SYAORAN_H |
36 |
#define _LINUX_SYAORAN_H |
37 |
|
38 |
#include <linux/version.h> |
39 |
|
40 |
#define false 0 |
41 |
#define true 1 |
42 |
|
43 |
#ifndef __user |
44 |
#define __user |
45 |
#endif |
46 |
|
47 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0) |
48 |
#define s_fs_info u.generic_sbp |
49 |
#else |
50 |
#include <linux/audit.h> |
51 |
#ifdef AUDIT_APPARMOR_AUDIT |
52 |
/* AppArmor patch adds "struct vfsmount" to VFS helper functions. */ |
53 |
#define HAVE_VFSMOUNT_IN_VFS_HELPER |
54 |
#endif |
55 |
#endif |
56 |
|
57 |
#if defined(RHEL_MAJOR) && RHEL_MAJOR == 5 |
58 |
#define HAVE_NO_I_BLKSIZE_IN_INODE |
59 |
#elif defined(AX_MAJOR) && AX_MAJOR == 3 |
60 |
#define HAVE_NO_I_BLKSIZE_IN_INODE |
61 |
#endif |
62 |
|
63 |
#ifndef DEFINE_SPINLOCK |
64 |
#define DEFINE_SPINLOCK(x) spinlock_t x = SPIN_LOCK_UNLOCKED |
65 |
#endif |
66 |
|
67 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
68 |
#define bool _Bool |
69 |
#endif |
70 |
|
71 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 14) |
72 |
#define kzalloc(size, flags) ({ \ |
73 |
void *ret = kmalloc((size), (flags)); \ |
74 |
if (ret) \ |
75 |
memset(ret, 0, (size)); \ |
76 |
ret; }) |
77 |
#endif |
78 |
|
79 |
#ifndef list_for_each_entry_safe |
80 |
#define list_for_each_entry_safe(pos, n, head, member) \ |
81 |
for (pos = list_entry((head)->next, typeof(*pos), member), \ |
82 |
n = list_entry(pos->member.next, typeof(*pos), member); \ |
83 |
&pos->member != (head); \ |
84 |
pos = n, n = list_entry(n->member.next, typeof(*n), member)) |
85 |
#endif |
86 |
|
87 |
/** |
88 |
* list_for_each_cookie - iterate over a list with cookie. |
89 |
* @pos: the &struct list_head to use as a loop cursor. |
90 |
* @cookie: the &struct list_head to use as a cookie. |
91 |
* @head: the head for your list. |
92 |
* |
93 |
* Same with list_for_each except that this primitive uses cookie |
94 |
* so that we can continue iteration. |
95 |
*/ |
96 |
#define list_for_each_cookie(pos, cookie, head) \ |
97 |
for (({ if (!cookie) \ |
98 |
cookie = head; }), pos = (cookie)->next; \ |
99 |
prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ |
100 |
(cookie) = pos, pos = pos->next) |
101 |
|
102 |
/* The following constants are used to restrict operations.*/ |
103 |
#define MAY_CREATE 1 /* This file is allowed to mknod() */ |
104 |
#define MAY_DELETE 2 /* This file is allowed to unlink() */ |
105 |
#define MAY_CHMOD 4 /* This file is allowed to chmod() */ |
106 |
#define MAY_CHOWN 8 /* This file is allowed to chown() */ |
107 |
#define DEVICE_USED 16 /* This block or character device file is used. */ |
108 |
#define NO_CREATE_AT_MOUNT 32 /* Don't create this file at mount(). */ |
109 |
|
110 |
/* some random number */ |
111 |
#define SYAORAN_MAGIC 0x2F646576 /* = '/dev' */ |
112 |
|
113 |
static void syaoran_put_super(struct super_block *sb); |
114 |
static int syaoran_initialize(struct super_block *sb, void *data); |
115 |
static void syaoran_make_initial_nodes(struct super_block *sb); |
116 |
static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev); |
117 |
static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags); |
118 |
static int syaoran_create_tracelog(struct super_block *sb, |
119 |
const char *filename); |
120 |
|
121 |
/* Wraps blkdev_open() to trace open operation for block devices. */ |
122 |
static int (*org_blkdev_open) (struct inode *inode, struct file *filp); |
123 |
static struct file_operations wrapped_def_blk_fops; |
124 |
|
125 |
static int wrapped_blkdev_open(struct inode *inode, struct file *filp) |
126 |
{ |
127 |
int error = org_blkdev_open(inode, filp); |
128 |
if (error != -ENXIO) |
129 |
syaoran_may_modify_node(filp->f_dentry, DEVICE_USED); |
130 |
return error; |
131 |
} |
132 |
|
133 |
/* Wraps chrdev_open() to trace open operation for character devices. */ |
134 |
static int (*org_chrdev_open) (struct inode *inode, struct file *filp); |
135 |
static struct file_operations wrapped_def_chr_fops; |
136 |
|
137 |
static int wrapped_chrdev_open(struct inode *inode, struct file *filp) |
138 |
{ |
139 |
int error = org_chrdev_open(inode, filp); |
140 |
if (error != -ENXIO) |
141 |
syaoran_may_modify_node(filp->f_dentry, DEVICE_USED); |
142 |
return error; |
143 |
} |
144 |
|
145 |
/* lookup_create() without nameidata. Called only while initialization. */ |
146 |
static struct dentry *lookup_create2(const char *name, struct dentry *base, |
147 |
const bool is_dir) |
148 |
{ |
149 |
struct dentry *dentry; |
150 |
const int len = name ? strlen(name) : 0; |
151 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
152 |
mutex_lock(&base->d_inode->i_mutex); |
153 |
#else |
154 |
down(&base->d_inode->i_sem); |
155 |
#endif |
156 |
dentry = lookup_one_len(name, base, len); |
157 |
if (IS_ERR(dentry)) |
158 |
goto fail; |
159 |
if (!is_dir && name[len] && !dentry->d_inode) |
160 |
goto enoent; |
161 |
return dentry; |
162 |
enoent: |
163 |
dput(dentry); |
164 |
dentry = ERR_PTR(-ENOENT); |
165 |
fail: |
166 |
return dentry; |
167 |
} |
168 |
|
169 |
/* mkdir(). Called only while initialization. */ |
170 |
static int fs_mkdir(const char *pathname, struct dentry *base, int mode, |
171 |
uid_t user, gid_t group) |
172 |
{ |
173 |
struct dentry *dentry = lookup_create2(pathname, base, 1); |
174 |
int error = PTR_ERR(dentry); |
175 |
if (!IS_ERR(dentry)) { |
176 |
#ifdef HAVE_VFSMOUNT_IN_VFS_HELPER |
177 |
error = vfs_mkdir(base->d_inode, dentry, NULL, mode); |
178 |
#else |
179 |
error = vfs_mkdir(base->d_inode, dentry, mode); |
180 |
#endif |
181 |
if (!error) { |
182 |
lock_kernel(); |
183 |
dentry->d_inode->i_uid = user; |
184 |
dentry->d_inode->i_gid = group; |
185 |
unlock_kernel(); |
186 |
} |
187 |
dput(dentry); |
188 |
} |
189 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
190 |
mutex_unlock(&base->d_inode->i_mutex); |
191 |
#else |
192 |
up(&base->d_inode->i_sem); |
193 |
#endif |
194 |
return error; |
195 |
} |
196 |
|
197 |
/* mknod(). Called only while initialization. */ |
198 |
static int fs_mknod(const char *filename, struct dentry *base, int mode, |
199 |
dev_t dev, uid_t user, gid_t group) |
200 |
{ |
201 |
struct dentry *dentry; |
202 |
int error; |
203 |
switch (mode & S_IFMT) { |
204 |
case S_IFCHR: |
205 |
case S_IFBLK: |
206 |
case S_IFIFO: |
207 |
case S_IFSOCK: |
208 |
case S_IFREG: |
209 |
break; |
210 |
default: |
211 |
return -EPERM; |
212 |
} |
213 |
dentry = lookup_create2(filename, base, 0); |
214 |
error = PTR_ERR(dentry); |
215 |
if (!IS_ERR(dentry)) { |
216 |
#ifdef HAVE_VFSMOUNT_IN_VFS_HELPER |
217 |
error = vfs_mknod(base->d_inode, dentry, NULL, mode, dev); |
218 |
#else |
219 |
error = vfs_mknod(base->d_inode, dentry, mode, dev); |
220 |
#endif |
221 |
if (!error) { |
222 |
lock_kernel(); |
223 |
dentry->d_inode->i_uid = user; |
224 |
dentry->d_inode->i_gid = group; |
225 |
unlock_kernel(); |
226 |
} |
227 |
dput(dentry); |
228 |
} |
229 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
230 |
mutex_unlock(&base->d_inode->i_mutex); |
231 |
#else |
232 |
up(&base->d_inode->i_sem); |
233 |
#endif |
234 |
return error; |
235 |
} |
236 |
|
237 |
/* symlink(). Called only while initialization. */ |
238 |
static int fs_symlink(const char *pathname, struct dentry *base, char *oldname, |
239 |
int mode, uid_t user, gid_t group) |
240 |
{ |
241 |
struct dentry *dentry = lookup_create2(pathname, base, 0); |
242 |
int error = PTR_ERR(dentry); |
243 |
if (!IS_ERR(dentry)) { |
244 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) && LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26) |
245 |
#ifdef HAVE_VFSMOUNT_IN_VFS_HELPER |
246 |
error = vfs_symlink(base->d_inode, dentry, NULL, oldname, |
247 |
S_IALLUGO); |
248 |
#else |
249 |
error = vfs_symlink(base->d_inode, dentry, oldname, S_IALLUGO); |
250 |
#endif |
251 |
#else |
252 |
#ifdef HAVE_VFSMOUNT_IN_VFS_HELPER |
253 |
error = vfs_symlink(base->d_inode, dentry, NULL, oldname); |
254 |
#else |
255 |
error = vfs_symlink(base->d_inode, dentry, oldname); |
256 |
#endif |
257 |
#endif |
258 |
if (!error) { |
259 |
lock_kernel(); |
260 |
dentry->d_inode->i_mode = mode; |
261 |
dentry->d_inode->i_uid = user; |
262 |
dentry->d_inode->i_gid = group; |
263 |
unlock_kernel(); |
264 |
} |
265 |
dput(dentry); |
266 |
} |
267 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
268 |
mutex_unlock(&base->d_inode->i_mutex); |
269 |
#else |
270 |
up(&base->d_inode->i_sem); |
271 |
#endif |
272 |
return error; |
273 |
} |
274 |
|
275 |
/* |
276 |
* Format string. |
277 |
* Leading and trailing whitespaces are removed. |
278 |
* Multiple whitespaces are packed into single space. |
279 |
*/ |
280 |
static void normalize_line(unsigned char *buffer) |
281 |
{ |
282 |
unsigned char *sp = buffer; |
283 |
unsigned char *dp = buffer; |
284 |
bool first = true; |
285 |
while (*sp && (*sp <= ' ' || *sp >= 127)) |
286 |
sp++; |
287 |
while (*sp) { |
288 |
if (!first) |
289 |
*dp++ = ' '; |
290 |
first = false; |
291 |
while (*sp > ' ' && *sp < 127) |
292 |
*dp++ = *sp++; |
293 |
while (*sp && (*sp <= ' ' || *sp >= 127)) |
294 |
sp++; |
295 |
} |
296 |
*dp = '\0'; |
297 |
} |
298 |
|
299 |
/* Convert text form of filename into binary form. */ |
300 |
static void unescape(char *filename) |
301 |
{ |
302 |
char *cp = filename; |
303 |
char c; |
304 |
char d; |
305 |
char e; |
306 |
if (!cp) |
307 |
return; |
308 |
while (1) { |
309 |
c = *filename++; |
310 |
if (!c) |
311 |
break; |
312 |
if (c != '\\') { |
313 |
*cp++ = c; |
314 |
continue; |
315 |
} |
316 |
c = *filename++; |
317 |
if (c == '\\') { |
318 |
*cp++ = c; |
319 |
continue; |
320 |
} |
321 |
if (c < '0' || c > '3') |
322 |
break; |
323 |
d = *filename++; |
324 |
if (d < '0' || d > '7') |
325 |
break; |
326 |
e = *filename++; |
327 |
if (e < '0' || e > '7') |
328 |
break; |
329 |
*(unsigned char *) cp++ = (unsigned char) |
330 |
(((unsigned char) (c - '0') << 6) |
331 |
+ ((unsigned char) (d - '0') << 3) |
332 |
+ (unsigned char) (e - '0')); |
333 |
} |
334 |
*cp = '\0'; |
335 |
} |
336 |
|
337 |
static char *strdup(const char *str) |
338 |
{ |
339 |
const int len = str ? strlen(str) + 1 : 0; |
340 |
char *cp = kzalloc(len, GFP_KERNEL); |
341 |
if (cp) |
342 |
memmove(cp, str, len); |
343 |
return cp; |
344 |
} |
345 |
|
346 |
/* -1: Not specified, 0: Enforce by default, 1: Accept by default. */ |
347 |
static int syaoran_default_mode = -1; |
348 |
|
349 |
#if !defined(MODULE) |
350 |
static int __init syaoran_setup(char *str) |
351 |
{ |
352 |
if (!strcmp(str, "accept")) |
353 |
syaoran_default_mode = 1; |
354 |
else if (!strcmp(str, "enforce")) |
355 |
syaoran_default_mode = 0; |
356 |
return 0; |
357 |
} |
358 |
|
359 |
__setup("SYAORAN=", syaoran_setup); |
360 |
#endif |
361 |
|
362 |
/* The structure for possible device list. */ |
363 |
struct dev_entry { |
364 |
struct list_head list; |
365 |
/* Binary form of pathname under mount point. Never NULL. */ |
366 |
char *name; |
367 |
/* |
368 |
* Mode and permissions. |
369 |
* setuid/setgid/sticky bits are not supported. |
370 |
*/ |
371 |
mode_t mode; |
372 |
uid_t uid; |
373 |
gid_t gid; |
374 |
dev_t kdev; |
375 |
/* |
376 |
* Binary form of initial contents for the symlink. NULL if not symlink. |
377 |
*/ |
378 |
char *symlink_data; |
379 |
/* File access control flags. */ |
380 |
unsigned int flags; |
381 |
/* Text form of pathname under mount point. Never NULL. */ |
382 |
const char *printable_name; |
383 |
/* |
384 |
* Text form of initial contents for the symlink. NULL if not symlink. |
385 |
*/ |
386 |
const char *printable_symlink_data; |
387 |
}; |
388 |
|
389 |
struct syaoran_sb_info { |
390 |
struct list_head list; |
391 |
bool initialize_done; /* False if initialization is in progress. */ |
392 |
bool is_permissive_mode; /* True if permissive mode. */ |
393 |
}; |
394 |
|
395 |
static int syaoran_register_node_info(char *buffer, struct super_block *sb) |
396 |
{ |
397 |
enum { |
398 |
ARG_FILENAME = 0, |
399 |
ARG_PERMISSION = 1, |
400 |
ARG_UID = 2, |
401 |
ARG_GID = 3, |
402 |
ARG_FLAGS = 4, |
403 |
ARG_DEV_TYPE = 5, |
404 |
ARG_SYMLINK_DATA = 6, |
405 |
ARG_DEV_MAJOR = 6, |
406 |
ARG_DEV_MINOR = 7, |
407 |
MAX_ARG = 8 |
408 |
}; |
409 |
char *args[MAX_ARG]; |
410 |
int i; |
411 |
int error = -EINVAL; |
412 |
unsigned int perm; |
413 |
unsigned int uid; |
414 |
unsigned int gid; |
415 |
unsigned int flags; |
416 |
unsigned int major = 0; |
417 |
unsigned int minor = 0; |
418 |
struct syaoran_sb_info *info = |
419 |
(struct syaoran_sb_info *) sb->s_fs_info; |
420 |
struct dev_entry *entry; |
421 |
if (!info) |
422 |
return -EINVAL; |
423 |
memset(args, 0, sizeof(args)); |
424 |
args[0] = buffer; |
425 |
for (i = 1; i < MAX_ARG; i++) { |
426 |
args[i] = strchr(args[i - 1] + 1, ' '); |
427 |
if (!args[i]) |
428 |
break; |
429 |
*args[i]++ = '\0'; |
430 |
} |
431 |
/* |
432 |
printk(KERN_DEBUG "<%s> <%s> <%s> <%s> <%s> <%s> <%s> <%s>\n", |
433 |
args[0], args[1], args[2], args[3], args[4], args[5], args[6], |
434 |
args[7]); |
435 |
*/ |
436 |
if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] || |
437 |
!args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_FLAGS]) |
438 |
goto out; |
439 |
if (sscanf(args[ARG_PERMISSION], "%o", &perm) != 1 || |
440 |
!(perm <= 0777) || sscanf(args[ARG_UID], "%u", &uid) != 1 || |
441 |
sscanf(args[ARG_GID], "%u", &gid) != 1 || |
442 |
sscanf(args[ARG_FLAGS], "%u", &flags) != 1 || |
443 |
*(args[ARG_DEV_TYPE] + 1)) |
444 |
goto out; |
445 |
switch (*args[ARG_DEV_TYPE]) { |
446 |
case 'c': |
447 |
perm |= S_IFCHR; |
448 |
if (!args[ARG_DEV_MAJOR] || |
449 |
sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 || |
450 |
!args[ARG_DEV_MINOR] || |
451 |
sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) |
452 |
goto out; |
453 |
break; |
454 |
case 'b': |
455 |
perm |= S_IFBLK; |
456 |
if (!args[ARG_DEV_MAJOR] || |
457 |
sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 || |
458 |
!args[ARG_DEV_MINOR] || |
459 |
sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) |
460 |
goto out; |
461 |
break; |
462 |
case 'l': |
463 |
perm |= S_IFLNK; |
464 |
if (!args[ARG_SYMLINK_DATA]) |
465 |
goto out; |
466 |
break; |
467 |
case 'd': |
468 |
perm |= S_IFDIR; |
469 |
break; |
470 |
case 's': |
471 |
perm |= S_IFSOCK; |
472 |
break; |
473 |
case 'p': |
474 |
perm |= S_IFIFO; |
475 |
break; |
476 |
case 'f': |
477 |
perm |= S_IFREG; |
478 |
break; |
479 |
default: |
480 |
goto out; |
481 |
} |
482 |
error = -ENOMEM; |
483 |
entry = kzalloc(sizeof(*entry), GFP_KERNEL); |
484 |
if (!entry) |
485 |
goto out; |
486 |
if (S_ISLNK(perm)) { |
487 |
entry->printable_symlink_data = strdup(args[ARG_SYMLINK_DATA]); |
488 |
if (!entry->printable_symlink_data) |
489 |
goto out_freemem; |
490 |
} |
491 |
entry->printable_name = strdup(args[ARG_FILENAME]); |
492 |
if (!entry->printable_name) |
493 |
goto out_freemem; |
494 |
if (S_ISLNK(perm)) { |
495 |
entry->symlink_data = strdup(entry->printable_symlink_data); |
496 |
if (!entry->symlink_data) |
497 |
goto out_freemem; |
498 |
unescape(entry->symlink_data); |
499 |
} |
500 |
entry->name = strdup(entry->printable_name); |
501 |
if (!entry->name) |
502 |
goto out_freemem; |
503 |
unescape(entry->name); |
504 |
{ |
505 |
/* |
506 |
* Drop trailing '/', for get_local_absolute_path() doesn't |
507 |
* append trailing '/'. |
508 |
*/ |
509 |
const int len = strlen(entry->name); |
510 |
if (len && entry->name[len - 1] == '/') |
511 |
entry->name[len - 1] = '\0'; |
512 |
} |
513 |
entry->mode = perm; |
514 |
entry->uid = uid; |
515 |
entry->gid = gid; |
516 |
entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0; |
517 |
entry->flags = flags; |
518 |
list_add_tail(&entry->list, &info->list); |
519 |
/* printk(KERN_DEBUG "Entry added.\n"); */ |
520 |
error = 0; |
521 |
out: |
522 |
return error; |
523 |
out_freemem: |
524 |
kfree(entry->printable_symlink_data); |
525 |
kfree(entry->printable_name); |
526 |
kfree(entry->symlink_data); |
527 |
kfree(entry); |
528 |
goto out; |
529 |
} |
530 |
|
531 |
static void syaoran_put_super(struct super_block *sb) |
532 |
{ |
533 |
struct syaoran_sb_info *info; |
534 |
struct dev_entry *entry; |
535 |
struct dev_entry *tmp; |
536 |
if (!sb) |
537 |
return; |
538 |
info = (struct syaoran_sb_info *) sb->s_fs_info; |
539 |
if (!info) |
540 |
return; |
541 |
sb->s_fs_info = NULL; |
542 |
list_for_each_entry_safe(entry, tmp, &info->list, list) { |
543 |
kfree(entry->name); |
544 |
kfree(entry->symlink_data); |
545 |
kfree(entry->printable_name); |
546 |
kfree(entry->printable_symlink_data); |
547 |
list_del(&entry->list); |
548 |
/* printk(KERN_DEBUG "Entry removed.\n"); */ |
549 |
kfree(entry); |
550 |
} |
551 |
kfree(info); |
552 |
printk(KERN_INFO "%s: Unused memory freed.\n", __func__); |
553 |
} |
554 |
|
555 |
static int syaoran_read_config_file(struct file *file, struct super_block *sb) |
556 |
{ |
557 |
char *buffer; |
558 |
int len; |
559 |
char *cp; |
560 |
unsigned long offset = 0; |
561 |
int error = -ENOMEM; |
562 |
if (!file) |
563 |
return -EINVAL; |
564 |
buffer = kzalloc(PAGE_SIZE, GFP_KERNEL); |
565 |
if (!buffer) |
566 |
goto out; |
567 |
while (1) { |
568 |
len = kernel_read(file, offset, buffer, PAGE_SIZE); |
569 |
if (len <= 0) |
570 |
break; |
571 |
cp = memchr(buffer, '\n', len); |
572 |
if (!cp) |
573 |
break; |
574 |
*cp = '\0'; |
575 |
offset += cp - buffer + 1; |
576 |
normalize_line(buffer); |
577 |
if (syaoran_register_node_info(buffer, sb) == -ENOMEM) |
578 |
goto out; |
579 |
} |
580 |
error = 0; |
581 |
out: |
582 |
kfree(buffer); |
583 |
return error; |
584 |
} |
585 |
|
586 |
static void syaoran_make_node(struct dev_entry *entry, struct dentry *root) |
587 |
{ |
588 |
struct dentry *base = dget(root); |
589 |
char *filename = entry->name; |
590 |
char *name = filename; |
591 |
unsigned int c; |
592 |
const mode_t perm = entry->mode; |
593 |
const uid_t uid = entry->uid; |
594 |
const gid_t gid = entry->gid; |
595 |
goto start; |
596 |
while (1) { |
597 |
c = *(unsigned char *) filename; |
598 |
if (!c) |
599 |
break; |
600 |
if (c == '/') { |
601 |
struct dentry *new_base; |
602 |
const int len = filename - name; |
603 |
*filename = '\0'; |
604 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
605 |
mutex_lock(&base->d_inode->i_mutex); |
606 |
new_base = lookup_one_len(name, base, len); |
607 |
mutex_unlock(&base->d_inode->i_mutex); |
608 |
#else |
609 |
down(&base->d_inode->i_sem); |
610 |
new_base = lookup_one_len(name, base, len); |
611 |
up(&base->d_inode->i_sem); |
612 |
#endif |
613 |
dput(base); |
614 |
*filename++ = '/'; |
615 |
if (IS_ERR(new_base)) { |
616 |
/* |
617 |
printk(KERN_DEBUG "'%s' = %ld\n", entry->name, |
618 |
PTR_ERR(new_base)); |
619 |
*/ |
620 |
return; |
621 |
} else if (!new_base->d_inode || |
622 |
!S_ISDIR(new_base->d_inode->i_mode)) { |
623 |
/* |
624 |
printk(KERN_DEBUG |
625 |
"Directory '%s' does not exist.\n", |
626 |
entry->name); |
627 |
*/ |
628 |
dput(new_base); |
629 |
return; |
630 |
} |
631 |
/* |
632 |
printk(KERN_DEBUG "Directory '%s' exists.\n", |
633 |
entry->name); |
634 |
*/ |
635 |
base = new_base; |
636 |
start: |
637 |
name = filename; |
638 |
} else { |
639 |
filename++; |
640 |
} |
641 |
} |
642 |
filename = (char *) name; |
643 |
if (S_ISLNK(perm)) |
644 |
fs_symlink(filename, base, entry->symlink_data, perm, uid, gid); |
645 |
else if (S_ISDIR(perm)) |
646 |
fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid); |
647 |
else if (S_ISSOCK(perm) || S_ISFIFO(perm) || S_ISREG(perm)) |
648 |
fs_mknod(filename, base, perm, 0, uid, gid); |
649 |
else if (S_ISCHR(perm) || S_ISBLK(perm)) |
650 |
fs_mknod(filename, base, perm, entry->kdev, uid, gid); |
651 |
dput(base); |
652 |
} |
653 |
|
654 |
/* Create files according to the policy file. */ |
655 |
static void syaoran_make_initial_nodes(struct super_block *sb) |
656 |
{ |
657 |
struct syaoran_sb_info *info; |
658 |
struct dev_entry *entry; |
659 |
if (!sb) |
660 |
return; |
661 |
info = (struct syaoran_sb_info *) sb->s_fs_info; |
662 |
if (!info) |
663 |
return; |
664 |
if (info->is_permissive_mode) { |
665 |
syaoran_create_tracelog(sb, ".syaoran"); |
666 |
syaoran_create_tracelog(sb, ".syaoran_all"); |
667 |
} |
668 |
list_for_each_entry(entry, &info->list, list) { |
669 |
if ((entry->flags & NO_CREATE_AT_MOUNT) == 0) |
670 |
syaoran_make_node(entry, sb->s_root); |
671 |
} |
672 |
info->initialize_done = true; |
673 |
} |
674 |
|
675 |
/* Read policy file. */ |
676 |
static int syaoran_initialize(struct super_block *sb, void *data) |
677 |
{ |
678 |
int error = -EINVAL; |
679 |
struct file *f; |
680 |
char *filename = (char *) data; |
681 |
bool is_permissive_mode = syaoran_default_mode; |
682 |
static bool first = true; |
683 |
if (first) { |
684 |
first = false; |
685 |
printk(KERN_INFO "SYAORAN: 1.6.6-pre 2008/12/01\n"); |
686 |
} |
687 |
{ |
688 |
struct inode *inode = new_inode(sb); |
689 |
if (!inode) |
690 |
return -EINVAL; |
691 |
/* Create /dev/ram0 to get the value of blkdev_open(). */ |
692 |
init_special_inode(inode, S_IFBLK | 0666, MKDEV(1, 0)); |
693 |
wrapped_def_blk_fops = *inode->i_fop; |
694 |
iput(inode); |
695 |
org_blkdev_open = wrapped_def_blk_fops.open; |
696 |
wrapped_def_blk_fops.open = wrapped_blkdev_open; |
697 |
} |
698 |
{ |
699 |
struct inode *inode = new_inode(sb); |
700 |
if (!inode) |
701 |
return -EINVAL; |
702 |
/* Create /dev/null to get the value of chrdev_open(). */ |
703 |
init_special_inode(inode, S_IFCHR | 0666, MKDEV(1, 3)); |
704 |
wrapped_def_chr_fops = *inode->i_fop; |
705 |
iput(inode); |
706 |
org_chrdev_open = wrapped_def_chr_fops.open; |
707 |
wrapped_def_chr_fops.open = wrapped_chrdev_open; |
708 |
} |
709 |
if (!filename) { |
710 |
printk(KERN_WARNING "SYAORAN: Missing config-file path.\n"); |
711 |
return -EINVAL; |
712 |
} |
713 |
/* If mode is given with mount operation, use it. */ |
714 |
if (!strncmp(filename, "accept=", 7)) { |
715 |
filename += 7; |
716 |
is_permissive_mode = true; |
717 |
} else if (!strncmp(filename, "enforce=", 8)) { |
718 |
filename += 8; |
719 |
is_permissive_mode = false; |
720 |
} else if (syaoran_default_mode == -1) { |
721 |
/* |
722 |
* If mode is not given with command line, |
723 |
* abort mount. |
724 |
*/ |
725 |
printk(KERN_WARNING |
726 |
"SYAORAN: Missing 'accept=' or 'enforce='.\n"); |
727 |
return -EINVAL; |
728 |
} |
729 |
f = filp_open(filename, O_RDONLY, 0600); |
730 |
if (IS_ERR(f)) { |
731 |
printk(KERN_WARNING "SYAORAN: Can't open '%s'\n", filename); |
732 |
return -EINVAL; |
733 |
} |
734 |
if (!S_ISREG(f->f_dentry->d_inode->i_mode)) |
735 |
goto out; |
736 |
sb->s_fs_info = kzalloc(sizeof(struct syaoran_sb_info), GFP_KERNEL); |
737 |
if (!sb->s_fs_info) |
738 |
goto out; |
739 |
((struct syaoran_sb_info *) sb->s_fs_info)->is_permissive_mode |
740 |
= is_permissive_mode; |
741 |
INIT_LIST_HEAD(&((struct syaoran_sb_info *) sb->s_fs_info)->list); |
742 |
printk(KERN_INFO "SYAORAN: Reading '%s'\n", filename); |
743 |
error = syaoran_read_config_file(f, sb); |
744 |
out: |
745 |
if (error) |
746 |
printk(KERN_WARNING "SYAORAN: Can't read '%s'\n", filename); |
747 |
filp_close(f, NULL); |
748 |
return error; |
749 |
} |
750 |
|
751 |
/* Get absolute pathname from mount point. */ |
752 |
static int get_local_absolute_path(struct dentry *dentry, char *buffer, |
753 |
int buflen) |
754 |
{ |
755 |
/***** CRITICAL SECTION START *****/ |
756 |
char *start = buffer; |
757 |
char *end = buffer + buflen; |
758 |
int namelen; |
759 |
|
760 |
if (buflen < 256) |
761 |
goto out; |
762 |
|
763 |
*--end = '\0'; |
764 |
buflen--; |
765 |
for (;;) { |
766 |
struct dentry *parent; |
767 |
if (IS_ROOT(dentry)) |
768 |
break; |
769 |
parent = dentry->d_parent; |
770 |
namelen = dentry->d_name.len; |
771 |
buflen -= namelen + 1; |
772 |
if (buflen < 0) |
773 |
goto out; |
774 |
end -= namelen; |
775 |
memcpy(end, dentry->d_name.name, namelen); |
776 |
*--end = '/'; |
777 |
dentry = parent; |
778 |
} |
779 |
if (*end == '/') { |
780 |
buflen++; |
781 |
end++; |
782 |
} |
783 |
namelen = dentry->d_name.len; |
784 |
buflen -= namelen; |
785 |
if (buflen < 0) |
786 |
goto out; |
787 |
end -= namelen; |
788 |
memcpy(end, dentry->d_name.name, namelen); |
789 |
memmove(start, end, strlen(end) + 1); |
790 |
return 0; |
791 |
out: |
792 |
return -ENOMEM; |
793 |
/***** CRITICAL SECTION END *****/ |
794 |
} |
795 |
|
796 |
/* Get absolute pathname of the given dentry from mount point. */ |
797 |
static int local_ccs_realpath_from_dentry(struct dentry *dentry, char *newname, |
798 |
int newname_len) |
799 |
{ |
800 |
/***** CRITICAL SECTION START *****/ |
801 |
int error; |
802 |
struct dentry *d_dentry; |
803 |
if (!dentry || !newname || newname_len <= 0) |
804 |
return -EINVAL; |
805 |
d_dentry = dget(dentry); |
806 |
spin_lock(&dcache_lock); |
807 |
error = get_local_absolute_path(d_dentry, newname, newname_len); |
808 |
spin_unlock(&dcache_lock); |
809 |
dput(d_dentry); |
810 |
return error; |
811 |
/***** CRITICAL SECTION END *****/ |
812 |
} |
813 |
|
814 |
static int syaoran_check_flags(struct syaoran_sb_info *info, |
815 |
struct dentry *dentry, |
816 |
int mode, int dev, unsigned int flags) |
817 |
{ |
818 |
int error; |
819 |
/* |
820 |
* I use static buffer, for local_ccs_realpath_from_dentry() needs |
821 |
* dcache_lock. |
822 |
*/ |
823 |
static char filename[PAGE_SIZE]; |
824 |
static DEFINE_SPINLOCK(lock); |
825 |
/***** CRITICAL SECTION START *****/ |
826 |
spin_lock(&lock); |
827 |
memset(filename, 0, sizeof(filename)); |
828 |
error = local_ccs_realpath_from_dentry(dentry, filename, |
829 |
sizeof(filename) - 1); |
830 |
if (!error) { |
831 |
struct dev_entry *entry; |
832 |
error = -EPERM; |
833 |
list_for_each_entry(entry, &info->list, list) { |
834 |
if ((mode & S_IFMT) != (entry->mode & S_IFMT)) |
835 |
continue; |
836 |
if ((S_ISBLK(mode) || S_ISCHR(mode)) && |
837 |
dev != entry->kdev) |
838 |
continue; |
839 |
if (strcmp(entry->name, filename + 1)) |
840 |
continue; |
841 |
if (info->is_permissive_mode) { |
842 |
entry->flags |= flags; |
843 |
error = 0; |
844 |
} else if ((entry->flags & flags) == flags) |
845 |
error = 0; |
846 |
break; |
847 |
} |
848 |
} |
849 |
if (!error) { |
850 |
const char *name; |
851 |
struct task_struct *task = current; |
852 |
const uid_t uid = task->fsuid; |
853 |
const gid_t gid = task->fsgid; |
854 |
const mode_t perm = mode & 0777; |
855 |
flags &= ~DEVICE_USED; |
856 |
{ |
857 |
char *end = filename + sizeof(filename) - 1; |
858 |
const char *cp = filename + strlen(filename) - 1; |
859 |
while (cp > filename && end > cp && |
860 |
end > filename + 16) { |
861 |
const unsigned char c = *cp--; |
862 |
if (c == '\\') { |
863 |
*--end = '\\'; |
864 |
*--end = '\\'; |
865 |
} else if (c > ' ' && c < 127) { |
866 |
*--end = c; |
867 |
} else { |
868 |
*--end = (c & 7) + '0'; |
869 |
*--end = ((c >> 3) & 7) + '0'; |
870 |
*--end = (c >> 6) + '0'; |
871 |
*--end = '\\'; |
872 |
} |
873 |
} |
874 |
name = end; |
875 |
} |
876 |
switch (mode & S_IFMT) { |
877 |
case S_IFCHR: |
878 |
printk(KERN_DEBUG |
879 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", |
880 |
name, perm, uid, gid, flags, 'c', |
881 |
MAJOR(dev), MINOR(dev)); |
882 |
break; |
883 |
case S_IFBLK: |
884 |
printk(KERN_DEBUG |
885 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", |
886 |
name, perm, uid, gid, flags, 'b', |
887 |
MAJOR(dev), MINOR(dev)); |
888 |
break; |
889 |
case S_IFIFO: |
890 |
printk(KERN_DEBUG |
891 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, |
892 |
perm, uid, gid, flags, 'p'); |
893 |
break; |
894 |
case S_IFSOCK: |
895 |
printk(KERN_DEBUG |
896 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, |
897 |
perm, uid, gid, flags, 's'); |
898 |
break; |
899 |
case S_IFDIR: |
900 |
printk(KERN_DEBUG |
901 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, |
902 |
perm, uid, gid, flags, 'd'); |
903 |
break; |
904 |
case S_IFLNK: |
905 |
printk(KERN_DEBUG |
906 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c %s\n", |
907 |
name, perm, uid, gid, flags, 'l', "unknown"); |
908 |
break; |
909 |
case S_IFREG: |
910 |
printk(KERN_DEBUG |
911 |
"SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, |
912 |
perm, uid, gid, flags, 'f'); |
913 |
break; |
914 |
} |
915 |
} |
916 |
spin_unlock(&lock); |
917 |
/***** CRITICAL SECTION END *****/ |
918 |
return error; |
919 |
} |
920 |
|
921 |
/* Check whether the given dentry is allowed to mknod. */ |
922 |
static int syaoran_may_create_node(struct dentry *dentry, int mode, int dev) |
923 |
{ |
924 |
struct syaoran_sb_info *info |
925 |
= (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
926 |
if (!info) { |
927 |
printk(KERN_WARNING "%s: dentry->d_sb->s_fs_info == NULL\n", |
928 |
__func__); |
929 |
return -EPERM; |
930 |
} |
931 |
if (!info->initialize_done) |
932 |
return 0; |
933 |
return syaoran_check_flags(info, dentry, mode, dev, MAY_CREATE); |
934 |
} |
935 |
|
936 |
/* Check whether the given dentry is allowed to chmod/chown/unlink. */ |
937 |
static int syaoran_may_modify_node(struct dentry *dentry, unsigned int flags) |
938 |
{ |
939 |
struct syaoran_sb_info *info |
940 |
= (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
941 |
if (!info) { |
942 |
printk(KERN_WARNING "%s: dentry->d_sb->s_fs_info == NULL\n", |
943 |
__func__); |
944 |
return -EPERM; |
945 |
} |
946 |
if (flags == DEVICE_USED && !info->is_permissive_mode) |
947 |
return 0; |
948 |
if (!dentry->d_inode) |
949 |
return -ENOENT; |
950 |
return syaoran_check_flags(info, dentry, dentry->d_inode->i_mode, |
951 |
dentry->d_inode->i_rdev, flags); |
952 |
} |
953 |
|
954 |
/* |
955 |
* The following structure and codes are used for transferring data |
956 |
* to interfaces files. |
957 |
*/ |
958 |
|
959 |
struct syaoran_read_struct { |
960 |
char *buf; /* Buffer for reading. */ |
961 |
int avail; /* Bytes available for reading. */ |
962 |
struct super_block *sb; /* The super_block of this partition. */ |
963 |
struct dev_entry *entry; /* The entry currently reading from. */ |
964 |
bool read_all; /* Print all entries? */ |
965 |
struct list_head *pos; /* Current position. */ |
966 |
}; |
967 |
|
968 |
static void syaoran_read_table(struct syaoran_read_struct *head, char *buf, |
969 |
int count) |
970 |
{ |
971 |
struct super_block *sb = head->sb; |
972 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info; |
973 |
struct list_head *pos; |
974 |
const bool read_all = head->read_all; |
975 |
if (!info) |
976 |
return; |
977 |
if (!head->pos) |
978 |
return; |
979 |
list_for_each_cookie(pos, head->pos, &info->list) { |
980 |
struct dev_entry *entry |
981 |
= list_entry(pos, struct dev_entry, list); |
982 |
const unsigned int flags |
983 |
= read_all ? entry->flags : entry->flags & ~DEVICE_USED; |
984 |
const char *name = entry->printable_name; |
985 |
const uid_t uid = entry->uid; |
986 |
const gid_t gid = entry->gid; |
987 |
const mode_t perm = entry->mode & 0777; |
988 |
int len = 0; |
989 |
switch (entry->mode & S_IFMT) { |
990 |
case S_IFCHR: |
991 |
if (!head->read_all && !(entry->flags & DEVICE_USED)) |
992 |
break; |
993 |
len = snprintf(buf, count, |
994 |
"%-20s %3o %3u %3u %2u %c %3u %3u\n", |
995 |
name, perm, uid, gid, flags, 'c', |
996 |
MAJOR(entry->kdev), MINOR(entry->kdev)); |
997 |
break; |
998 |
case S_IFBLK: |
999 |
if (!head->read_all && !(entry->flags & DEVICE_USED)) |
1000 |
break; |
1001 |
len = snprintf(buf, count, |
1002 |
"%-20s %3o %3u %3u %2u %c %3u %3u\n", |
1003 |
name, perm, uid, gid, flags, 'b', |
1004 |
MAJOR(entry->kdev), MINOR(entry->kdev)); |
1005 |
break; |
1006 |
case S_IFIFO: |
1007 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", |
1008 |
name, perm, uid, gid, flags, 'p'); |
1009 |
break; |
1010 |
case S_IFSOCK: |
1011 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", |
1012 |
name, perm, uid, gid, flags, 's'); |
1013 |
break; |
1014 |
case S_IFDIR: |
1015 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", |
1016 |
name, perm, uid, gid, flags, 'd'); |
1017 |
break; |
1018 |
case S_IFLNK: |
1019 |
len = snprintf(buf, count, |
1020 |
"%-20s %3o %3u %3u %2u %c %s\n", name, |
1021 |
perm, uid, gid, flags, 'l', |
1022 |
entry->printable_symlink_data); |
1023 |
break; |
1024 |
case S_IFREG: |
1025 |
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", |
1026 |
name, perm, uid, gid, flags, 'f'); |
1027 |
break; |
1028 |
} |
1029 |
if (len < 0 || count <= len) |
1030 |
break; |
1031 |
count -= len; |
1032 |
buf += len; |
1033 |
head->avail += len; |
1034 |
} |
1035 |
} |
1036 |
|
1037 |
static int syaoran_trace_open(struct inode *inode, struct file *file) |
1038 |
{ |
1039 |
struct syaoran_read_struct *head = kzalloc(sizeof(*head), GFP_KERNEL); |
1040 |
if (!head) |
1041 |
return -ENOMEM; |
1042 |
head->sb = inode->i_sb; |
1043 |
head->read_all |
1044 |
= (strcmp(file->f_dentry->d_name.name, ".syaoran_all") == 0); |
1045 |
head->pos = &((struct syaoran_sb_info *) head->sb->s_fs_info)->list; |
1046 |
/* Don't allow open() after unmount() */ |
1047 |
if (head->sb->s_fs_info) |
1048 |
head->buf = kzalloc(PAGE_SIZE * 2, GFP_KERNEL); |
1049 |
if (!head->buf) { |
1050 |
kfree(head); |
1051 |
return -ENOMEM; |
1052 |
} |
1053 |
file->private_data = head; |
1054 |
return 0; |
1055 |
} |
1056 |
|
1057 |
static int syaoran_trace_release(struct inode *inode, struct file *file) |
1058 |
{ |
1059 |
struct syaoran_read_struct *head = file->private_data; |
1060 |
kfree(head->buf); |
1061 |
kfree(head); |
1062 |
file->private_data = NULL; |
1063 |
return 0; |
1064 |
} |
1065 |
|
1066 |
static ssize_t syaoran_trace_read(struct file *file, char __user *buf, |
1067 |
size_t count, loff_t *ppos) |
1068 |
{ |
1069 |
struct syaoran_read_struct *head |
1070 |
= (struct syaoran_read_struct *) file->private_data; |
1071 |
int len = head->avail; |
1072 |
char *cp = head->buf; |
1073 |
if (!access_ok(VERIFY_WRITE, buf, count)) |
1074 |
return -EFAULT; |
1075 |
syaoran_read_table(head, cp + len, PAGE_SIZE * 2 - len); |
1076 |
len = head->avail; |
1077 |
if (len > count) |
1078 |
len = count; |
1079 |
if (len > 0) { |
1080 |
if (copy_to_user(buf, cp, len)) |
1081 |
return -EFAULT; |
1082 |
head->avail -= len; |
1083 |
memmove(cp, cp + len, head->avail); |
1084 |
} |
1085 |
return len; |
1086 |
} |
1087 |
|
1088 |
static struct file_operations syaoran_trace_operations = { |
1089 |
.open = syaoran_trace_open, |
1090 |
.release = syaoran_trace_release, |
1091 |
.read = syaoran_trace_read, |
1092 |
}; |
1093 |
|
1094 |
/* Create interface files for reading status. */ |
1095 |
static int syaoran_create_tracelog(struct super_block *sb, const char *filename) |
1096 |
{ |
1097 |
struct dentry *base = dget(sb->s_root); |
1098 |
struct dentry *dentry = lookup_create2(filename, base, 0); |
1099 |
int error = PTR_ERR(dentry); |
1100 |
if (!IS_ERR(dentry)) { |
1101 |
struct inode *inode = new_inode(sb); |
1102 |
if (inode) { |
1103 |
inode->i_mode = S_IFREG | 0400; |
1104 |
inode->i_uid = 0; |
1105 |
inode->i_gid = 0; |
1106 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) |
1107 |
#ifndef HAVE_NO_I_BLKSIZE_IN_INODE |
1108 |
inode->i_blksize = PAGE_CACHE_SIZE; |
1109 |
#endif |
1110 |
#endif |
1111 |
inode->i_blocks = 0; |
1112 |
inode->i_mapping->a_ops = &syaoran_aops; |
1113 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0) |
1114 |
inode->i_mapping->backing_dev_info |
1115 |
= &syaoran_backing_dev_info; |
1116 |
inode->i_op = &syaoran_file_inode_operations; |
1117 |
#else |
1118 |
inode->i_rdev = NODEV; |
1119 |
#endif |
1120 |
inode->i_ctime = CURRENT_TIME; |
1121 |
inode->i_mtime = inode->i_ctime; |
1122 |
inode->i_atime = inode->i_mtime; |
1123 |
inode->i_fop = &syaoran_trace_operations; |
1124 |
d_instantiate(dentry, inode); |
1125 |
dget(dentry); /* Extra count - pin the dentry in core */ |
1126 |
error = 0; |
1127 |
} |
1128 |
dput(dentry); |
1129 |
} |
1130 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
1131 |
mutex_unlock(&base->d_inode->i_mutex); |
1132 |
#else |
1133 |
up(&base->d_inode->i_sem); |
1134 |
#endif |
1135 |
dput(base); |
1136 |
return error; |
1137 |
} |
1138 |
|
1139 |
#endif |