1 |
kumaneko |
111 |
/* |
2 |
|
|
* include/linux/syaoran.h |
3 |
|
|
* |
4 |
|
|
* Implementation of the Tamper-Proof Device Filesystem. |
5 |
|
|
* |
6 |
|
|
* Copyright (C) 2005-2007 NTT DATA CORPORATION |
7 |
|
|
* |
8 |
kumaneko |
224 |
* Version: 1.4.1-rc1 2007/05/20 |
9 |
kumaneko |
111 |
* |
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 Nexus". |
18 |
|
|
* SYAORAN is a filesystem for /dev with Mandatory Access Control. |
19 |
|
|
* |
20 |
|
|
* /dev needs to be writable, but this means that files on /dev might be tampered with. |
21 |
|
|
* SYAORAN can restrict combinations of (pathname, attribute) that the system can create. |
22 |
|
|
* The attribute is one of directory, regular file, FIFO, UNIX domain socket, |
23 |
|
|
* symbolic link, character or block device file with major/minor device numbers. |
24 |
|
|
* |
25 |
|
|
* You can use SYAORAN alone, but I recommend you to use with SAKURA and TOMOYO. |
26 |
|
|
*/ |
27 |
|
|
|
28 |
|
|
#ifndef _LINUX_SYAORAN_H |
29 |
|
|
#define _LINUX_SYAORAN_H |
30 |
|
|
|
31 |
|
|
#ifndef __user |
32 |
|
|
#define __user |
33 |
|
|
#endif |
34 |
|
|
|
35 |
|
|
/***** SYAORAN start. *****/ |
36 |
|
|
|
37 |
|
|
#include <linux/version.h> |
38 |
|
|
|
39 |
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) |
40 |
|
|
#define s_fs_info u.generic_sbp |
41 |
|
|
#endif |
42 |
|
|
|
43 |
|
|
/* The following constants are used to restrict operations.*/ |
44 |
|
|
|
45 |
|
|
#define MAY_CREATE 1 /* This file is allowed to mknod() */ |
46 |
|
|
#define MAY_DELETE 2 /* This file is allowed to unlink() */ |
47 |
|
|
#define MAY_CHMOD 4 /* This file is allowed to chmod() */ |
48 |
|
|
#define MAY_CHOWN 8 /* This file is allowed to chown() */ |
49 |
|
|
#define DEVICE_USED 16 /* This block or character device file is used. */ |
50 |
|
|
#define NO_CREATE_AT_MOUNT 32 /* Don't create this file at mount(). */ |
51 |
|
|
|
52 |
|
|
/* some random number */ |
53 |
|
|
#define SYAORAN_MAGIC 0x2F646576 /* = '/dev' */ |
54 |
|
|
|
55 |
|
|
static void syaoran_put_super(struct super_block *sb); |
56 |
|
|
static int Syaoran_Initialize(struct super_block *sb, void *data); |
57 |
|
|
static void MakeInitialNodes(struct super_block *sb); |
58 |
|
|
static int MayCreateNode(struct dentry *dentry, int mode, int dev); |
59 |
|
|
static int MayModifyNode(struct dentry *dentry, unsigned int flags); |
60 |
|
|
static int syaoran_create_tracelog(struct super_block *sb, const char *filename); |
61 |
|
|
|
62 |
|
|
/* Wraps blkdev_open() to trace open operation for block devices. */ |
63 |
|
|
static int (*org_blkdev_open) (struct inode * inode, struct file * filp) = NULL; |
64 |
|
|
static struct file_operations wrapped_def_blk_fops; |
65 |
|
|
|
66 |
|
|
static int wrapped_blkdev_open(struct inode * inode, struct file * filp) |
67 |
|
|
{ |
68 |
|
|
int error = org_blkdev_open(inode, filp); |
69 |
|
|
if (error != -ENXIO) MayModifyNode(filp->f_dentry, DEVICE_USED); |
70 |
|
|
return error; |
71 |
|
|
} |
72 |
|
|
|
73 |
|
|
/* Wraps chrdev_open() to trace open operation for character devices. */ |
74 |
|
|
static int (*org_chrdev_open) (struct inode * inode, struct file * filp) = NULL; |
75 |
|
|
static struct file_operations wrapped_def_chr_fops; |
76 |
|
|
|
77 |
|
|
static int wrapped_chrdev_open(struct inode * inode, struct file * filp) |
78 |
|
|
{ |
79 |
|
|
int error = org_chrdev_open(inode, filp); |
80 |
|
|
if (error != -ENXIO) MayModifyNode(filp->f_dentry, DEVICE_USED); |
81 |
|
|
return error; |
82 |
|
|
} |
83 |
|
|
|
84 |
|
|
/* lookup_create() without nameidata. Called only while initialization. */ |
85 |
|
|
static struct dentry *lookup_create2(const char *name, struct dentry *base, int is_dir) |
86 |
|
|
{ |
87 |
|
|
struct dentry *dentry; |
88 |
|
|
const int len = name ? strlen(name) : 0; |
89 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
90 |
|
|
mutex_lock(&base->d_inode->i_mutex); |
91 |
|
|
#else |
92 |
|
|
down(&base->d_inode->i_sem); |
93 |
|
|
#endif |
94 |
|
|
dentry = lookup_one_len(name, base, len); |
95 |
|
|
if (IS_ERR(dentry)) goto fail; |
96 |
|
|
if (!is_dir && name[len] && !dentry->d_inode) goto enoent; |
97 |
|
|
return dentry; |
98 |
|
|
enoent: |
99 |
|
|
dput(dentry); |
100 |
|
|
dentry = ERR_PTR(-ENOENT); |
101 |
|
|
fail: |
102 |
|
|
return dentry; |
103 |
|
|
} |
104 |
|
|
|
105 |
|
|
/* mkdir(). Called only while initialization. */ |
106 |
|
|
static int fs_mkdir(const char *pathname, struct dentry *base, int mode, uid_t user, gid_t group) |
107 |
|
|
{ |
108 |
|
|
struct dentry *dentry = lookup_create2(pathname, base, 1); |
109 |
|
|
int error = PTR_ERR(dentry); |
110 |
|
|
if (!IS_ERR(dentry)) { |
111 |
|
|
error = vfs_mkdir(base->d_inode, dentry, mode); |
112 |
|
|
if (!error) { |
113 |
|
|
lock_kernel(); |
114 |
|
|
dentry->d_inode->i_uid = user; |
115 |
|
|
dentry->d_inode->i_gid = group; |
116 |
|
|
unlock_kernel(); |
117 |
|
|
} |
118 |
|
|
dput(dentry); |
119 |
|
|
} |
120 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
121 |
|
|
mutex_unlock(&base->d_inode->i_mutex); |
122 |
|
|
#else |
123 |
|
|
up(&base->d_inode->i_sem); |
124 |
|
|
#endif |
125 |
|
|
return error; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
|
/* mknod(). Called only while initialization. */ |
129 |
|
|
static int fs_mknod(const char *filename, struct dentry *base, int mode, dev_t dev, uid_t user, gid_t group) |
130 |
|
|
{ |
131 |
|
|
struct dentry *dentry; |
132 |
|
|
int error; |
133 |
|
|
switch (mode & S_IFMT) { |
134 |
|
|
case S_IFCHR: case S_IFBLK: case S_IFIFO: case S_IFSOCK: case S_IFREG: |
135 |
|
|
break; |
136 |
|
|
default: |
137 |
|
|
return -EPERM; |
138 |
|
|
} |
139 |
|
|
dentry = lookup_create2(filename, base, 0); |
140 |
|
|
error = PTR_ERR(dentry); |
141 |
|
|
if (!IS_ERR(dentry)) { |
142 |
|
|
error = vfs_mknod(base->d_inode, dentry, mode, dev); |
143 |
|
|
if (!error) { |
144 |
|
|
lock_kernel(); |
145 |
|
|
dentry->d_inode->i_uid = user; |
146 |
|
|
dentry->d_inode->i_gid = group; |
147 |
|
|
unlock_kernel(); |
148 |
|
|
} |
149 |
|
|
dput(dentry); |
150 |
|
|
} |
151 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
152 |
|
|
mutex_unlock(&base->d_inode->i_mutex); |
153 |
|
|
#else |
154 |
|
|
up(&base->d_inode->i_sem); |
155 |
|
|
#endif |
156 |
|
|
return error; |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
/* symlink(). Called only while initialization. */ |
160 |
|
|
static int fs_symlink(const char *pathname, struct dentry *base, char *oldname, int mode, uid_t user, gid_t group) |
161 |
|
|
{ |
162 |
|
|
struct dentry *dentry = lookup_create2(pathname, base, 0); |
163 |
|
|
int error = PTR_ERR(dentry); |
164 |
|
|
if (!IS_ERR(dentry)) { |
165 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
166 |
|
|
error = vfs_symlink(base->d_inode, dentry, oldname, S_IALLUGO); |
167 |
|
|
#else |
168 |
|
|
error = vfs_symlink(base->d_inode, dentry, oldname); |
169 |
|
|
#endif |
170 |
|
|
if (!error) { |
171 |
|
|
lock_kernel(); |
172 |
|
|
dentry->d_inode->i_mode = mode; |
173 |
|
|
dentry->d_inode->i_uid = user; |
174 |
|
|
dentry->d_inode->i_gid = group; |
175 |
|
|
unlock_kernel(); |
176 |
|
|
} |
177 |
|
|
dput(dentry); |
178 |
|
|
} |
179 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
180 |
|
|
mutex_unlock(&base->d_inode->i_mutex); |
181 |
|
|
#else |
182 |
|
|
up(&base->d_inode->i_sem); |
183 |
|
|
#endif |
184 |
|
|
return error; |
185 |
|
|
} |
186 |
|
|
|
187 |
|
|
/* |
188 |
|
|
* Format string. |
189 |
|
|
* Leading and trailing whitespaces are removed. |
190 |
|
|
* Multiple whitespaces are packed into single space. |
191 |
|
|
*/ |
192 |
|
|
static void NormalizeLine(unsigned char *buffer) |
193 |
|
|
{ |
194 |
|
|
unsigned char *sp = buffer, *dp = buffer; |
195 |
|
|
int first = 1; |
196 |
|
|
while (*sp && (*sp <= ' ' || *sp >= 127)) sp++; |
197 |
|
|
while (*sp) { |
198 |
|
|
if (!first) *dp++ = ' '; |
199 |
|
|
first = 0; |
200 |
|
|
while (*sp > ' ' && *sp < 127) *dp++ = *sp++; |
201 |
|
|
while (*sp && (*sp <= ' ' || *sp >= 127)) sp++; |
202 |
|
|
} |
203 |
|
|
*dp = '\0'; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
/* Convert text form of filename into binary form. */ |
207 |
|
|
static void UnEscape(char *filename) |
208 |
|
|
{ |
209 |
|
|
char *cp = filename; |
210 |
|
|
char c, d, e; |
211 |
|
|
if (!cp) return; |
212 |
|
|
while ((c = *filename++) != '\0') { |
213 |
|
|
if (c != '\\') { |
214 |
|
|
*cp++ = c; |
215 |
|
|
continue; |
216 |
|
|
} |
217 |
|
|
if ((c = *filename++) == '\\') { |
218 |
|
|
*cp++ = c; |
219 |
|
|
continue; |
220 |
|
|
} |
221 |
|
|
if (c < '0' || c > '3' || |
222 |
|
|
(d = *filename++) < '0' || d > '7' || |
223 |
|
|
(e = *filename++) < '0' || e > '7') { |
224 |
|
|
break; |
225 |
|
|
} |
226 |
|
|
* (unsigned char *) cp++ = (unsigned char) (((unsigned char) (c - '0') << 6) + ((unsigned char) (d - '0') << 3) + (unsigned char) (e - '0')); |
227 |
|
|
} |
228 |
|
|
*cp = '\0'; |
229 |
|
|
} |
230 |
|
|
|
231 |
|
|
static char *strdup(const char *str) |
232 |
|
|
{ |
233 |
|
|
char *cp; |
234 |
|
|
const int len = str ? strlen(str) + 1 : 0; |
235 |
|
|
if ((cp = kmalloc(len, GFP_KERNEL)) != NULL) memmove(cp, str, len); |
236 |
|
|
return cp; |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
static int syaoran_default_mode = -1; /* -1: Not specified, 0: Enforce by default, 1: Accept by default. */ |
240 |
|
|
|
241 |
|
|
#if !defined(MODULE) |
242 |
|
|
static int __init SYAORAN_Setup(char *str) |
243 |
|
|
{ |
244 |
|
|
if (strcmp(str, "accept") == 0) syaoran_default_mode = 1; |
245 |
|
|
else if (strcmp(str, "enforce") == 0) syaoran_default_mode = 0; |
246 |
|
|
return 0; |
247 |
|
|
} |
248 |
|
|
|
249 |
|
|
__setup("SYAORAN=", SYAORAN_Setup); |
250 |
|
|
#endif |
251 |
|
|
|
252 |
|
|
struct dev_entry { |
253 |
|
|
struct dev_entry *next; /* Pointer to next record. NULL if none. */ |
254 |
|
|
char *name; /* Binary form of pathname under mount point. Never NULL. */ |
255 |
|
|
mode_t mode; /* Mode and permissions. setuid/setgid/sticky bits are not supported. */ |
256 |
|
|
uid_t uid; |
257 |
|
|
gid_t gid; |
258 |
|
|
dev_t kdev; |
259 |
|
|
char *symlink_data; /* Binary form of initial contents for the symlink. NULL if not symlink. */ |
260 |
|
|
unsigned int flags; /* File access control flags. */ |
261 |
|
|
const char *printable_name; /* Text form of pathname under mount point. Never NULL. */ |
262 |
|
|
const char *printable_symlink_data; /* Text form of initial contents for the symlink. NULL if not symlink. */ |
263 |
|
|
}; |
264 |
|
|
|
265 |
|
|
struct syaoran_sb_info { |
266 |
|
|
struct dev_entry *first_entry; /* Pointer to first file acl. NULL if not assigned. */ |
267 |
|
|
int initialize_done; /* Zero if initialization is in progress. */ |
268 |
|
|
int is_permissive_mode; /* Nonzero if permissive mode. */ |
269 |
|
|
}; |
270 |
|
|
|
271 |
|
|
static int RegisterNodeInfo(char *buffer, struct super_block *sb) |
272 |
|
|
{ |
273 |
|
|
enum { |
274 |
|
|
ARG_FILENAME = 0, |
275 |
|
|
ARG_PERMISSION = 1, |
276 |
|
|
ARG_UID = 2, |
277 |
|
|
ARG_GID = 3, |
278 |
|
|
ARG_FLAGS = 4, |
279 |
|
|
ARG_DEV_TYPE = 5, |
280 |
|
|
ARG_SYMLINK_DATA = 6, |
281 |
|
|
ARG_DEV_MAJOR = 6, |
282 |
|
|
ARG_DEV_MINOR = 7, |
283 |
|
|
MAX_ARG = 8 |
284 |
|
|
}; |
285 |
|
|
char *args[MAX_ARG]; |
286 |
|
|
int i; |
287 |
|
|
int error = -EINVAL; |
288 |
kumaneko |
118 |
unsigned int perm, uid, gid, flags, major = 0, minor = 0; |
289 |
kumaneko |
111 |
struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info; |
290 |
|
|
struct dev_entry *entry; |
291 |
|
|
memset(args, 0, sizeof(args)); |
292 |
|
|
args[0] = buffer; |
293 |
|
|
for (i = 1; i < MAX_ARG; i++) { |
294 |
|
|
args[i] = strchr(args[i - 1] + 1, ' '); |
295 |
|
|
if (!args[i]) break; |
296 |
|
|
*args[i]++ = '\0'; |
297 |
|
|
} |
298 |
|
|
/* printk("<%s> <%s> <%s> <%s> <%s> <%s> <%s> <%s>\n", args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); */ |
299 |
|
|
if (!args[ARG_FILENAME] || !args[ARG_PERMISSION] || !args[ARG_UID] || !args[ARG_GID] || !args[ARG_DEV_TYPE] || !args[ARG_FLAGS]) goto out; |
300 |
kumaneko |
118 |
if (sscanf(args[ARG_PERMISSION], "%o", &perm) != 1 || !(perm <= 0777) || sscanf(args[ARG_UID], "%u", &uid) != 1 |
301 |
kumaneko |
111 |
|| sscanf(args[ARG_GID], "%u", &gid) != 1 || sscanf(args[ARG_FLAGS], "%u", &flags) != 1 || *(args[ARG_DEV_TYPE] + 1)) goto out; |
302 |
|
|
switch (*args[ARG_DEV_TYPE]) { |
303 |
|
|
case 'c': |
304 |
|
|
perm |= S_IFCHR; |
305 |
|
|
if (!args[ARG_DEV_MAJOR] || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 |
306 |
|
|
|| !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) goto out; |
307 |
|
|
break; |
308 |
|
|
case 'b': |
309 |
|
|
perm |= S_IFBLK; |
310 |
|
|
if (!args[ARG_DEV_MAJOR] || sscanf(args[ARG_DEV_MAJOR], "%u", &major) != 1 |
311 |
|
|
|| !args[ARG_DEV_MINOR] || sscanf(args[ARG_DEV_MINOR], "%u", &minor) != 1) goto out; |
312 |
|
|
break; |
313 |
|
|
case 'l': |
314 |
|
|
perm |= S_IFLNK; |
315 |
|
|
if (!args[ARG_SYMLINK_DATA]) goto out; |
316 |
|
|
break; |
317 |
|
|
case 'd': |
318 |
|
|
perm |= S_IFDIR; |
319 |
|
|
break; |
320 |
|
|
case 's': |
321 |
|
|
perm |= S_IFSOCK; |
322 |
|
|
break; |
323 |
|
|
case 'p': |
324 |
|
|
perm |= S_IFIFO; |
325 |
|
|
break; |
326 |
|
|
case 'f': |
327 |
|
|
perm |= S_IFREG; |
328 |
|
|
break; |
329 |
|
|
default: |
330 |
|
|
goto out; |
331 |
|
|
} |
332 |
|
|
error = -ENOMEM; |
333 |
kumaneko |
214 |
if ((entry = kmalloc(sizeof(*entry), GFP_KERNEL)) == NULL) goto out; |
334 |
|
|
memset(entry, 0, sizeof(*entry)); |
335 |
kumaneko |
111 |
if (!info->first_entry) { |
336 |
|
|
info->first_entry = entry; |
337 |
|
|
} else { |
338 |
|
|
struct dev_entry *p = info->first_entry; |
339 |
|
|
while (p->next) p = p->next; p->next = entry; |
340 |
|
|
} |
341 |
|
|
if (S_ISLNK(perm)) { |
342 |
|
|
if ((entry->printable_symlink_data = strdup(args[ARG_SYMLINK_DATA])) == NULL) goto out; |
343 |
|
|
} |
344 |
|
|
if ((entry->printable_name = strdup(args[ARG_FILENAME])) == NULL) goto out_freemem; |
345 |
|
|
if (S_ISLNK(perm)) { |
346 |
|
|
if ((entry->symlink_data = strdup(entry->printable_symlink_data)) == NULL) goto out_freemem; |
347 |
|
|
UnEscape(entry->symlink_data); |
348 |
|
|
} |
349 |
|
|
if ((entry->name = strdup(entry->printable_name)) == NULL) goto out_freemem; |
350 |
|
|
UnEscape(entry->name); |
351 |
|
|
{ /* Drop trailing '/', for GetLocalAbsolutePath() doesn't append trailing '/'. */ |
352 |
|
|
const int len = strlen(entry->name); |
353 |
|
|
if (len && entry->name[len - 1] == '/') entry->name[len - 1] = '\0'; |
354 |
|
|
} |
355 |
|
|
entry->mode = perm; |
356 |
|
|
entry->uid = uid; |
357 |
|
|
entry->gid = gid; |
358 |
|
|
entry->kdev = S_ISCHR(perm) || S_ISBLK(perm) ? MKDEV(major, minor) : 0; |
359 |
|
|
entry->flags = flags; |
360 |
|
|
/* printk("Entry added.\n"); */ |
361 |
|
|
error = 0; |
362 |
|
|
out: |
363 |
|
|
return error; |
364 |
|
|
out_freemem: |
365 |
|
|
kfree(entry->printable_symlink_data); |
366 |
|
|
kfree(entry->printable_name); |
367 |
|
|
kfree(entry->symlink_data); |
368 |
|
|
goto out; |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
static void syaoran_put_super(struct super_block *sb) |
372 |
|
|
{ |
373 |
|
|
struct syaoran_sb_info *info; |
374 |
|
|
struct dev_entry *entry; |
375 |
|
|
if (!sb) return; |
376 |
|
|
info = (struct syaoran_sb_info *) sb->s_fs_info; |
377 |
|
|
if (!info) return; |
378 |
|
|
entry = info->first_entry; |
379 |
|
|
while (entry) { |
380 |
|
|
struct dev_entry *next = entry->next; |
381 |
|
|
kfree(entry->name); entry->name = NULL; |
382 |
|
|
kfree(entry->symlink_data); entry->symlink_data = NULL; |
383 |
|
|
kfree(entry->printable_name); entry->printable_name = NULL; |
384 |
|
|
kfree(entry->printable_symlink_data); entry->printable_symlink_data = NULL; |
385 |
|
|
kfree(entry); entry = next; |
386 |
|
|
/* printk("Entry removed.\n"); */ |
387 |
|
|
} |
388 |
|
|
info->first_entry = NULL; |
389 |
|
|
kfree(info); |
390 |
|
|
sb->s_fs_info = NULL; |
391 |
|
|
printk("%s: Unused memory freed.\n", __FUNCTION__); |
392 |
|
|
} |
393 |
|
|
|
394 |
|
|
static int ReadConfigFile(struct file *file, struct super_block *sb) |
395 |
|
|
{ |
396 |
|
|
char *buffer; |
397 |
|
|
int error = -ENOMEM; |
398 |
|
|
if (!file) return -EINVAL; |
399 |
|
|
if ((buffer = kmalloc(PAGE_SIZE, GFP_KERNEL)) != NULL) { |
400 |
|
|
int len; |
401 |
|
|
char *cp; |
402 |
|
|
unsigned long offset = 0; |
403 |
|
|
memset(buffer, 0, PAGE_SIZE); |
404 |
|
|
while ((len = kernel_read(file, offset, buffer, PAGE_SIZE)) > 0 && (cp = memchr(buffer, '\n', len)) != NULL) { |
405 |
|
|
*cp = '\0'; |
406 |
|
|
offset += cp - buffer + 1; |
407 |
|
|
NormalizeLine(buffer); |
408 |
|
|
if (RegisterNodeInfo(buffer, sb) == -ENOMEM) goto out; |
409 |
|
|
} |
410 |
|
|
error = 0; |
411 |
|
|
} |
412 |
|
|
out: |
413 |
|
|
kfree(buffer); |
414 |
|
|
return error; |
415 |
|
|
} |
416 |
|
|
|
417 |
|
|
static void MakeNode(struct dev_entry *entry, struct dentry *root) |
418 |
|
|
{ |
419 |
|
|
struct dentry *base = dget(root); |
420 |
|
|
char *filename = entry->name; |
421 |
|
|
char *name = filename; |
422 |
|
|
unsigned int c; |
423 |
|
|
const mode_t perm = entry->mode; |
424 |
|
|
const uid_t uid = entry->uid; |
425 |
|
|
const gid_t gid = entry->gid; |
426 |
|
|
goto start; |
427 |
|
|
while ((c = * (unsigned char *) filename) != '\0') { |
428 |
|
|
if (c == '/') { |
429 |
|
|
struct dentry *new_base; |
430 |
|
|
const int len = filename - name; |
431 |
|
|
*filename = '\0'; |
432 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
433 |
|
|
mutex_lock(&base->d_inode->i_mutex); |
434 |
|
|
new_base = lookup_one_len(name, base, len); |
435 |
|
|
mutex_unlock(&base->d_inode->i_mutex); |
436 |
|
|
#else |
437 |
|
|
down(&base->d_inode->i_sem); |
438 |
|
|
new_base = lookup_one_len(name, base, len); |
439 |
|
|
up(&base->d_inode->i_sem); |
440 |
|
|
#endif |
441 |
|
|
dput(base); |
442 |
|
|
/* |
443 |
|
|
if (IS_ERR(new_base)) { |
444 |
|
|
printk("'%s' = %ld\n", entry->name, PTR_ERR(new_base)); |
445 |
|
|
} else if (!new_base->d_inode || !S_ISDIR(new_base->d_inode->i_mode)) { |
446 |
|
|
printk("Directory '%s' does not exist.\n", entry->name); |
447 |
|
|
} else { |
448 |
|
|
printk("Directory '%s' exists.\n", entry->name); |
449 |
|
|
} |
450 |
|
|
*/ |
451 |
|
|
*filename = '/'; |
452 |
|
|
filename++; |
453 |
|
|
if (IS_ERR(new_base)) { |
454 |
|
|
return; |
455 |
|
|
} else if (!new_base->d_inode || !S_ISDIR(new_base->d_inode->i_mode)) { |
456 |
|
|
dput(new_base); |
457 |
|
|
return; |
458 |
|
|
} |
459 |
|
|
base = new_base; |
460 |
|
|
start: |
461 |
|
|
name = filename; |
462 |
|
|
} else { |
463 |
|
|
filename++; |
464 |
|
|
} |
465 |
|
|
} |
466 |
|
|
filename = (char *) name; |
467 |
|
|
if (S_ISLNK(perm)) { |
468 |
|
|
fs_symlink(filename, base, entry->symlink_data, perm, uid, gid); |
469 |
|
|
} else if (S_ISDIR(perm)) { |
470 |
|
|
fs_mkdir(filename, base, perm ^ S_IFDIR, uid, gid); |
471 |
|
|
} else if (S_ISSOCK(perm) || S_ISFIFO(perm) || S_ISREG(perm)) { |
472 |
|
|
fs_mknod(filename, base, perm, 0, uid, gid); |
473 |
|
|
} else if (S_ISCHR(perm) || S_ISBLK(perm)) { |
474 |
|
|
fs_mknod(filename, base, perm, entry->kdev, uid, gid); |
475 |
|
|
} |
476 |
|
|
dput(base); |
477 |
|
|
} |
478 |
|
|
|
479 |
|
|
/* Create files according to the policy file. */ |
480 |
|
|
static void MakeInitialNodes(struct super_block *sb) |
481 |
|
|
{ |
482 |
|
|
struct syaoran_sb_info *info; |
483 |
|
|
struct dev_entry *entry; |
484 |
|
|
if (!sb) return; |
485 |
|
|
info = (struct syaoran_sb_info *) sb->s_fs_info; |
486 |
|
|
if (!info) return; |
487 |
|
|
if (info->is_permissive_mode) { |
488 |
|
|
syaoran_create_tracelog(sb, ".syaoran"); |
489 |
|
|
syaoran_create_tracelog(sb, ".syaoran_all"); |
490 |
|
|
} |
491 |
|
|
for (entry = info->first_entry; entry; entry = entry->next) { |
492 |
|
|
if ((entry->flags & NO_CREATE_AT_MOUNT) == 0) MakeNode(entry, sb->s_root); |
493 |
|
|
} |
494 |
|
|
info->initialize_done = 1; |
495 |
|
|
} |
496 |
|
|
|
497 |
|
|
/* Read policy file. */ |
498 |
|
|
static int Syaoran_Initialize(struct super_block *sb, void *data) |
499 |
|
|
{ |
500 |
|
|
int error = -EINVAL; |
501 |
|
|
static int first = 1; |
502 |
|
|
if (first) { |
503 |
|
|
first = 0; |
504 |
kumaneko |
224 |
printk("SYAORAN: 1.4.1-rc1 2007/05/20\n"); |
505 |
kumaneko |
111 |
} |
506 |
|
|
{ |
507 |
|
|
struct inode *inode = new_inode(sb); |
508 |
|
|
if (!inode) return -EINVAL; |
509 |
|
|
/* Create /dev/ram0 to get the value of blkdev_open(). */ |
510 |
|
|
init_special_inode(inode, S_IFBLK | 0666, MKDEV(1, 0)); |
511 |
|
|
wrapped_def_blk_fops = *inode->i_fop; |
512 |
|
|
iput(inode); |
513 |
|
|
org_blkdev_open = wrapped_def_blk_fops.open; |
514 |
|
|
wrapped_def_blk_fops.open = wrapped_blkdev_open; |
515 |
|
|
} |
516 |
|
|
{ |
517 |
|
|
struct inode *inode = new_inode(sb); |
518 |
|
|
if (!inode) return -EINVAL; |
519 |
|
|
/* Create /dev/null to get the value of chrdev_open(). */ |
520 |
|
|
init_special_inode(inode, S_IFCHR | 0666, MKDEV(1, 3)); |
521 |
|
|
wrapped_def_chr_fops = *inode->i_fop; |
522 |
|
|
iput(inode); |
523 |
|
|
org_chrdev_open = wrapped_def_chr_fops.open; |
524 |
|
|
wrapped_def_chr_fops.open = wrapped_chrdev_open; |
525 |
|
|
} |
526 |
|
|
if (data) { |
527 |
|
|
struct file *f; |
528 |
|
|
char *filename = (char *) data; |
529 |
|
|
int is_permissive_mode = syaoran_default_mode; |
530 |
|
|
/* If mode is given with mount operation, use it. */ |
531 |
|
|
if (strncmp(filename, "accept=", 7) == 0) { |
532 |
|
|
filename += 7; |
533 |
|
|
is_permissive_mode = 1; |
534 |
|
|
} else if (strncmp(filename, "enforce=", 8) == 0) { |
535 |
|
|
filename += 8; |
536 |
|
|
is_permissive_mode = 0; |
537 |
|
|
} else if (is_permissive_mode == -1) { |
538 |
|
|
/* If mode is not given with command line, abort mount. */ |
539 |
|
|
printk("SYAORAN: Missing 'accept=' or 'enforce='.\n"); |
540 |
|
|
return -EINVAL; |
541 |
|
|
} |
542 |
|
|
f = filp_open(filename, O_RDONLY, 0600); |
543 |
|
|
if (!IS_ERR(f)) { |
544 |
|
|
struct syaoran_sb_info *p; |
545 |
|
|
if (!S_ISREG(f->f_dentry->d_inode->i_mode)) goto out; |
546 |
kumaneko |
214 |
if ((p = sb->s_fs_info = kmalloc(sizeof(*p), GFP_KERNEL)) == NULL) goto out; |
547 |
|
|
memset(p, 0, sizeof(*p)); |
548 |
kumaneko |
111 |
p->is_permissive_mode = is_permissive_mode; |
549 |
|
|
printk("SYAORAN: Reading '%s'\n", filename); |
550 |
|
|
error = ReadConfigFile(f, sb); |
551 |
|
|
out: |
552 |
|
|
if (error) printk("SYAORAN: Can't read '%s'\n", filename); |
553 |
|
|
filp_close(f, NULL); |
554 |
|
|
} else { |
555 |
|
|
printk("SYAORAN: Can't open '%s'\n", filename); |
556 |
|
|
} |
557 |
|
|
} else { |
558 |
|
|
printk("SYAORAN: Missing config-file path.\n"); |
559 |
|
|
} |
560 |
|
|
return error; |
561 |
|
|
} |
562 |
|
|
|
563 |
|
|
/* Get absolute pathname from mount point. */ |
564 |
|
|
static int GetLocalAbsolutePath(struct dentry *dentry, char *buffer, int buflen) |
565 |
|
|
{ |
566 |
|
|
char *start = buffer; |
567 |
|
|
char *end = buffer + buflen; |
568 |
|
|
int namelen; |
569 |
|
|
|
570 |
|
|
if (buflen < 256) goto out; |
571 |
|
|
|
572 |
|
|
*--end = '\0'; |
573 |
|
|
buflen--; |
574 |
|
|
for (;;) { |
575 |
|
|
struct dentry *parent; |
576 |
|
|
if (IS_ROOT(dentry)) break; |
577 |
|
|
parent = dentry->d_parent; |
578 |
|
|
namelen = dentry->d_name.len; |
579 |
|
|
buflen -= namelen + 1; |
580 |
|
|
if (buflen < 0) goto out; |
581 |
|
|
end -= namelen; |
582 |
|
|
memcpy(end, dentry->d_name.name, namelen); |
583 |
|
|
*--end = '/'; |
584 |
|
|
dentry = parent; |
585 |
|
|
} |
586 |
|
|
if (*end == '/') { buflen++; end++; } |
587 |
|
|
namelen = dentry->d_name.len; |
588 |
|
|
buflen -= namelen; |
589 |
|
|
if (buflen < 0) goto out; |
590 |
|
|
end -= namelen; |
591 |
|
|
memcpy(end, dentry->d_name.name, namelen); |
592 |
|
|
memmove(start, end, strlen(end) + 1); |
593 |
|
|
return 0; |
594 |
|
|
out: |
595 |
|
|
return -ENOMEM; |
596 |
|
|
} |
597 |
|
|
|
598 |
|
|
/* Get absolute pathname of the given dentry from mount point. */ |
599 |
|
|
static int local_realpath_from_dentry(struct dentry *dentry, char *newname, int newname_len) |
600 |
|
|
{ |
601 |
|
|
int error; |
602 |
|
|
struct dentry *d_dentry; |
603 |
|
|
if (!dentry || !newname || newname_len <= 0) return -EINVAL; |
604 |
|
|
d_dentry = dget(dentry); |
605 |
|
|
/***** CRITICAL SECTION START *****/ |
606 |
|
|
spin_lock(&dcache_lock); |
607 |
|
|
error = GetLocalAbsolutePath(d_dentry, newname, newname_len); |
608 |
|
|
spin_unlock(&dcache_lock); |
609 |
|
|
/***** CRITICAL SECTION END *****/ |
610 |
|
|
dput(d_dentry); |
611 |
|
|
return error; |
612 |
|
|
} |
613 |
|
|
|
614 |
|
|
static int CheckFlags(struct syaoran_sb_info *info, struct dentry *dentry, int mode, int dev, unsigned int flags) |
615 |
|
|
{ |
616 |
|
|
int error = -EPERM; |
617 |
|
|
/* I use static buffer, for local_realpath_from_dentry() needs dcache_lock. */ |
618 |
|
|
static char filename[PAGE_SIZE]; |
619 |
|
|
static spinlock_t lock = SPIN_LOCK_UNLOCKED; |
620 |
|
|
spin_lock(&lock); |
621 |
|
|
memset(filename, 0, sizeof(filename)); |
622 |
|
|
if (local_realpath_from_dentry(dentry, filename, sizeof(filename) - 1) == 0) { |
623 |
|
|
struct dev_entry *entry; |
624 |
|
|
for (entry = info->first_entry; entry; entry = entry->next) { |
625 |
|
|
if ((mode & S_IFMT) != (entry->mode & S_IFMT)) continue; |
626 |
|
|
if ((S_ISBLK(mode) || S_ISCHR(mode)) && dev != entry->kdev) continue; |
627 |
|
|
if (strcmp(entry->name, filename + 1)) continue; |
628 |
|
|
if (info->is_permissive_mode) { |
629 |
|
|
entry->flags |= flags; |
630 |
|
|
error = 0; |
631 |
|
|
} else { |
632 |
|
|
if ((entry->flags & flags) == flags) error = 0; |
633 |
|
|
} |
634 |
|
|
break; |
635 |
|
|
} |
636 |
|
|
} |
637 |
|
|
if (error && strlen(filename) < (sizeof(filename) / 4) - 16) { |
638 |
|
|
const char *name; |
639 |
|
|
const uid_t uid = current->fsuid; |
640 |
|
|
const gid_t gid = current->fsgid; |
641 |
|
|
const mode_t perm = mode & 0777; |
642 |
|
|
flags &= ~DEVICE_USED; |
643 |
|
|
{ |
644 |
|
|
char *end = filename + sizeof(filename) - 1; |
645 |
|
|
const char *cp = strchr(filename, '\0') - 1; |
646 |
|
|
while (cp > filename) { |
647 |
|
|
const unsigned char c = *cp--; |
648 |
|
|
if (c == '\\') { |
649 |
|
|
*--end = '\\'; |
650 |
|
|
*--end = '\\'; |
651 |
|
|
} else if (c > ' ' && c < 127) { |
652 |
|
|
*--end = c; |
653 |
|
|
} else { |
654 |
|
|
*--end = (c & 7) + '0'; |
655 |
|
|
*--end = ((c >> 3) & 7) + '0'; |
656 |
|
|
*--end = (c >> 6) + '0'; |
657 |
|
|
*--end = '\\'; |
658 |
|
|
} |
659 |
|
|
} |
660 |
|
|
name = end; |
661 |
|
|
} |
662 |
|
|
switch (mode & S_IFMT) { |
663 |
|
|
case S_IFCHR: |
664 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'c', MAJOR(dev), MINOR(dev)); |
665 |
|
|
break; |
666 |
|
|
case S_IFBLK: |
667 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'b', MAJOR(dev), MINOR(dev)); |
668 |
|
|
break; |
669 |
|
|
case S_IFIFO: |
670 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'p'); |
671 |
|
|
break; |
672 |
|
|
case S_IFSOCK: |
673 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 's'); |
674 |
|
|
break; |
675 |
|
|
case S_IFDIR: |
676 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'd'); |
677 |
|
|
break; |
678 |
|
|
case S_IFLNK: |
679 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c %s\n", name, perm, uid, gid, flags, 'l', "unknown"); |
680 |
|
|
break; |
681 |
|
|
case S_IFREG: |
682 |
|
|
printk(KERN_DEBUG "SYAORAN-ERROR: %s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'f'); |
683 |
|
|
break; |
684 |
|
|
} |
685 |
|
|
} |
686 |
|
|
spin_unlock(&lock); |
687 |
|
|
return error; |
688 |
|
|
} |
689 |
|
|
|
690 |
|
|
/* Check whether the given dentry is allowed to mknod. */ |
691 |
|
|
static int MayCreateNode(struct dentry *dentry, int mode, int dev) |
692 |
|
|
{ |
693 |
|
|
struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
694 |
|
|
if (!info) { |
695 |
|
|
printk("%s: dentry->d_sb->s_fs_info == NULL\n", __FUNCTION__); |
696 |
|
|
return -EPERM; |
697 |
|
|
} |
698 |
|
|
if (!info->initialize_done) return 0; |
699 |
|
|
return CheckFlags(info, dentry, mode, dev, MAY_CREATE); |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
/* Check whether the given dentry is allowed to chmod/chown/unlink. */ |
703 |
|
|
static int MayModifyNode(struct dentry *dentry, unsigned int flags) |
704 |
|
|
{ |
705 |
|
|
struct syaoran_sb_info *info = (struct syaoran_sb_info *) dentry->d_sb->s_fs_info; |
706 |
|
|
if (!info) { |
707 |
|
|
printk("%s: dentry->d_sb->s_fs_info == NULL\n", __FUNCTION__); |
708 |
|
|
return -EPERM; |
709 |
|
|
} |
710 |
|
|
if (flags == DEVICE_USED && !info->is_permissive_mode) return 0; |
711 |
|
|
if (!dentry->d_inode) return -ENOENT; |
712 |
|
|
return CheckFlags(info, dentry, dentry->d_inode->i_mode, dentry->d_inode->i_rdev, flags); |
713 |
|
|
} |
714 |
|
|
|
715 |
|
|
/* |
716 |
|
|
* The following structure and codes are used for transferring data to interfaces files. |
717 |
|
|
*/ |
718 |
|
|
|
719 |
|
|
struct syaoran_read_struct { |
720 |
|
|
char *buf; /* Buffer for reading. */ |
721 |
|
|
int avail; /* Bytes available for reading. */ |
722 |
|
|
struct super_block *sb; /* The super_block of this partition. */ |
723 |
|
|
struct dev_entry *entry; /* The entry currently reading from. */ |
724 |
|
|
int read_all; /* Nonzero if dump all entries. */ |
725 |
|
|
}; |
726 |
|
|
|
727 |
|
|
static void ReadTable(struct syaoran_read_struct *head, char *buf, int count) |
728 |
|
|
{ |
729 |
|
|
struct super_block *sb = head->sb; |
730 |
|
|
struct syaoran_sb_info *info = (struct syaoran_sb_info *) sb->s_fs_info; |
731 |
|
|
struct dev_entry *entry; |
732 |
|
|
if (!info) return; |
733 |
|
|
for (entry = head->entry; entry; entry = entry->next) { |
734 |
|
|
const unsigned int flags = entry->flags & ~DEVICE_USED; |
735 |
|
|
const char *name = entry->printable_name; |
736 |
|
|
const uid_t uid = entry->uid; |
737 |
|
|
const gid_t gid = entry->gid; |
738 |
|
|
const mode_t perm = entry->mode & 0777; |
739 |
|
|
int len = 0; |
740 |
|
|
switch (entry->mode & S_IFMT) { |
741 |
|
|
case S_IFCHR: |
742 |
|
|
if (head->read_all) len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, entry->flags, 'c', MAJOR(entry->kdev), MINOR(entry->kdev)); |
743 |
|
|
else if (entry->flags & DEVICE_USED) len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'c', MAJOR(entry->kdev), MINOR(entry->kdev)); |
744 |
|
|
break; |
745 |
|
|
case S_IFBLK: |
746 |
|
|
if (head->read_all) len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, entry->flags, 'b', MAJOR(entry->kdev), MINOR(entry->kdev)); |
747 |
|
|
else if (entry->flags & DEVICE_USED) len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %3u %3u\n", name, perm, uid, gid, flags, 'b', MAJOR(entry->kdev), MINOR(entry->kdev)); |
748 |
|
|
break; |
749 |
|
|
case S_IFIFO: |
750 |
|
|
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'p'); |
751 |
|
|
break; |
752 |
|
|
case S_IFSOCK: |
753 |
|
|
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 's'); |
754 |
|
|
break; |
755 |
|
|
case S_IFDIR: |
756 |
|
|
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'd'); |
757 |
|
|
break; |
758 |
|
|
case S_IFLNK: |
759 |
|
|
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c %s\n", name, perm, uid, gid, flags, 'l', entry->printable_symlink_data); |
760 |
|
|
break; |
761 |
|
|
case S_IFREG: |
762 |
|
|
len = snprintf(buf, count, "%-20s %3o %3u %3u %2u %c\n", name, perm, uid, gid, flags, 'f'); |
763 |
|
|
break; |
764 |
|
|
} |
765 |
|
|
if (len < 0 || count < len) break; |
766 |
|
|
count -= len; |
767 |
|
|
buf += len; |
768 |
|
|
head->avail += len; |
769 |
|
|
head->entry = entry->next; |
770 |
|
|
} |
771 |
|
|
} |
772 |
|
|
|
773 |
|
|
static int syaoran_trace_open(struct inode *inode, struct file *file) |
774 |
|
|
{ |
775 |
|
|
struct syaoran_read_struct *head; |
776 |
kumaneko |
214 |
if ((head = kmalloc(sizeof(*head), GFP_KERNEL)) == NULL) return -ENOMEM; |
777 |
|
|
memset(head, 0, sizeof(*head)); |
778 |
kumaneko |
111 |
head->sb = inode->i_sb; |
779 |
|
|
head->read_all = (strcmp(file->f_dentry->d_name.name, ".syaoran_all") == 0); |
780 |
|
|
head->entry = ((struct syaoran_sb_info *) head->sb->s_fs_info)->first_entry; |
781 |
|
|
if ((head->buf = kmalloc(PAGE_SIZE * 2, GFP_KERNEL)) == NULL) { |
782 |
|
|
kfree(head); |
783 |
|
|
return -ENOMEM; |
784 |
|
|
} |
785 |
|
|
memset(head->buf, 0, PAGE_SIZE * 2); |
786 |
|
|
file->private_data = head; |
787 |
|
|
return 0; |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
static int syaoran_trace_release(struct inode *inode, struct file *file) |
791 |
|
|
{ |
792 |
|
|
struct syaoran_read_struct *head = file->private_data; |
793 |
|
|
kfree(head->buf); |
794 |
|
|
kfree(head); |
795 |
|
|
file->private_data = NULL; |
796 |
|
|
return 0; |
797 |
|
|
} |
798 |
|
|
|
799 |
|
|
static ssize_t syaoran_trace_read(struct file *file, char __user *buf, size_t count, loff_t * ppos) |
800 |
|
|
{ |
801 |
|
|
struct syaoran_read_struct *head = (struct syaoran_read_struct *) file->private_data; |
802 |
|
|
int len = head->avail; |
803 |
|
|
char *cp = head->buf; |
804 |
|
|
if (!access_ok(VERIFY_WRITE, buf, count)) return -EFAULT; |
805 |
|
|
ReadTable(head, cp + len, PAGE_SIZE * 2 - len); |
806 |
|
|
len = head->avail; |
807 |
|
|
if (len > count) len = count; |
808 |
|
|
if (len > 0) { |
809 |
|
|
if (copy_to_user(buf, cp, len)) return -EFAULT; |
810 |
|
|
head->avail -= len; |
811 |
|
|
memmove(cp, cp + len, head->avail); |
812 |
|
|
} |
813 |
|
|
return len; |
814 |
|
|
} |
815 |
|
|
|
816 |
|
|
static struct file_operations syaoran_trace_operations = { |
817 |
|
|
open: syaoran_trace_open, |
818 |
|
|
release: syaoran_trace_release, |
819 |
|
|
read: syaoran_trace_read, |
820 |
|
|
}; |
821 |
|
|
|
822 |
|
|
/* Create interface files for reading status. */ |
823 |
|
|
static int syaoran_create_tracelog(struct super_block *sb, const char *filename) |
824 |
|
|
{ |
825 |
|
|
struct dentry *base = dget(sb->s_root); |
826 |
|
|
struct dentry *dentry = lookup_create2(filename, base, 0); |
827 |
|
|
int error = PTR_ERR(dentry); |
828 |
|
|
if (!IS_ERR(dentry)) { |
829 |
|
|
struct inode *inode = new_inode(sb); |
830 |
|
|
if (inode) { |
831 |
|
|
inode->i_mode = S_IFREG | 0400; |
832 |
|
|
inode->i_uid = 0; |
833 |
|
|
inode->i_gid = 0; |
834 |
|
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) |
835 |
|
|
inode->i_blksize = PAGE_CACHE_SIZE; |
836 |
|
|
#endif |
837 |
|
|
inode->i_blocks = 0; |
838 |
|
|
inode->i_mapping->a_ops = &syaoran_aops; |
839 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) |
840 |
|
|
inode->i_mapping->backing_dev_info = &syaoran_backing_dev_info; |
841 |
|
|
inode->i_op = &syaoran_file_inode_operations; |
842 |
|
|
#else |
843 |
|
|
inode->i_rdev = NODEV; |
844 |
|
|
#endif |
845 |
|
|
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; |
846 |
|
|
inode->i_fop = &syaoran_trace_operations; |
847 |
|
|
d_instantiate(dentry, inode); |
848 |
|
|
dget(dentry); /* Extra count - pin the dentry in core */ |
849 |
|
|
error = 0; |
850 |
|
|
} |
851 |
|
|
dput(dentry); |
852 |
|
|
} |
853 |
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16) |
854 |
|
|
mutex_unlock(&base->d_inode->i_mutex); |
855 |
|
|
#else |
856 |
|
|
up(&base->d_inode->i_sem); |
857 |
|
|
#endif |
858 |
|
|
dput(base); |
859 |
|
|
return error; |
860 |
|
|
} |
861 |
|
|
|
862 |
|
|
/***** SYAORAN end. *****/ |
863 |
|
|
#endif |