diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/hostfs/hostfs_kern.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/hostfs/hostfs_kern.c')
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 1045 |
1 files changed, 1045 insertions, 0 deletions
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c new file mode 100644 index 000000000000..a88ad2924851 --- /dev/null +++ b/fs/hostfs/hostfs_kern.c | |||
@@ -0,0 +1,1045 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000, 2001, 2002 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | * | ||
5 | * Ported the filesystem routines to 2.5. | ||
6 | * 2003-02-10 Petr Baudis <pasky@ucw.cz> | ||
7 | */ | ||
8 | |||
9 | #include <linux/stddef.h> | ||
10 | #include <linux/fs.h> | ||
11 | #include <linux/version.h> | ||
12 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/pagemap.h> | ||
16 | #include <linux/blkdev.h> | ||
17 | #include <linux/list.h> | ||
18 | #include <linux/root_dev.h> | ||
19 | #include <linux/statfs.h> | ||
20 | #include <linux/kdev_t.h> | ||
21 | #include <asm/uaccess.h> | ||
22 | #include "hostfs.h" | ||
23 | #include "kern_util.h" | ||
24 | #include "kern.h" | ||
25 | #include "user_util.h" | ||
26 | #include "2_5compat.h" | ||
27 | #include "init.h" | ||
28 | |||
29 | struct hostfs_inode_info { | ||
30 | char *host_filename; | ||
31 | int fd; | ||
32 | int mode; | ||
33 | struct inode vfs_inode; | ||
34 | }; | ||
35 | |||
36 | static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) | ||
37 | { | ||
38 | return(list_entry(inode, struct hostfs_inode_info, vfs_inode)); | ||
39 | } | ||
40 | |||
41 | #define FILE_HOSTFS_I(file) HOSTFS_I((file)->f_dentry->d_inode) | ||
42 | |||
43 | int hostfs_d_delete(struct dentry *dentry) | ||
44 | { | ||
45 | return(1); | ||
46 | } | ||
47 | |||
48 | struct dentry_operations hostfs_dentry_ops = { | ||
49 | .d_delete = hostfs_d_delete, | ||
50 | }; | ||
51 | |||
52 | /* Changed in hostfs_args before the kernel starts running */ | ||
53 | static char *root_ino = "/"; | ||
54 | static int append = 0; | ||
55 | |||
56 | #define HOSTFS_SUPER_MAGIC 0x00c0ffee | ||
57 | |||
58 | static struct inode_operations hostfs_iops; | ||
59 | static struct inode_operations hostfs_dir_iops; | ||
60 | static struct address_space_operations hostfs_link_aops; | ||
61 | |||
62 | #ifndef MODULE | ||
63 | static int __init hostfs_args(char *options, int *add) | ||
64 | { | ||
65 | char *ptr; | ||
66 | |||
67 | ptr = strchr(options, ','); | ||
68 | if(ptr != NULL) | ||
69 | *ptr++ = '\0'; | ||
70 | if(*options != '\0') | ||
71 | root_ino = options; | ||
72 | |||
73 | options = ptr; | ||
74 | while(options){ | ||
75 | ptr = strchr(options, ','); | ||
76 | if(ptr != NULL) | ||
77 | *ptr++ = '\0'; | ||
78 | if(*options != '\0'){ | ||
79 | if(!strcmp(options, "append")) | ||
80 | append = 1; | ||
81 | else printf("hostfs_args - unsupported option - %s\n", | ||
82 | options); | ||
83 | } | ||
84 | options = ptr; | ||
85 | } | ||
86 | return(0); | ||
87 | } | ||
88 | |||
89 | __uml_setup("hostfs=", hostfs_args, | ||
90 | "hostfs=<root dir>,<flags>,...\n" | ||
91 | " This is used to set hostfs parameters. The root directory argument\n" | ||
92 | " is used to confine all hostfs mounts to within the specified directory\n" | ||
93 | " tree on the host. If this isn't specified, then a user inside UML can\n" | ||
94 | " mount anything on the host that's accessible to the user that's running\n" | ||
95 | " it.\n" | ||
96 | " The only flag currently supported is 'append', which specifies that all\n" | ||
97 | " files opened by hostfs will be opened in append mode.\n\n" | ||
98 | ); | ||
99 | #endif | ||
100 | |||
101 | static char *dentry_name(struct dentry *dentry, int extra) | ||
102 | { | ||
103 | struct dentry *parent; | ||
104 | char *root, *name; | ||
105 | int len; | ||
106 | |||
107 | len = 0; | ||
108 | parent = dentry; | ||
109 | while(parent->d_parent != parent){ | ||
110 | len += parent->d_name.len + 1; | ||
111 | parent = parent->d_parent; | ||
112 | } | ||
113 | |||
114 | root = HOSTFS_I(parent->d_inode)->host_filename; | ||
115 | len += strlen(root); | ||
116 | name = kmalloc(len + extra + 1, GFP_KERNEL); | ||
117 | if(name == NULL) return(NULL); | ||
118 | |||
119 | name[len] = '\0'; | ||
120 | parent = dentry; | ||
121 | while(parent->d_parent != parent){ | ||
122 | len -= parent->d_name.len + 1; | ||
123 | name[len] = '/'; | ||
124 | strncpy(&name[len + 1], parent->d_name.name, | ||
125 | parent->d_name.len); | ||
126 | parent = parent->d_parent; | ||
127 | } | ||
128 | strncpy(name, root, strlen(root)); | ||
129 | return(name); | ||
130 | } | ||
131 | |||
132 | static char *inode_name(struct inode *ino, int extra) | ||
133 | { | ||
134 | struct dentry *dentry; | ||
135 | |||
136 | dentry = list_entry(ino->i_dentry.next, struct dentry, d_alias); | ||
137 | return(dentry_name(dentry, extra)); | ||
138 | } | ||
139 | |||
140 | static int read_name(struct inode *ino, char *name) | ||
141 | { | ||
142 | /* The non-int inode fields are copied into ints by stat_file and | ||
143 | * then copied into the inode because passing the actual pointers | ||
144 | * in and having them treated as int * breaks on big-endian machines | ||
145 | */ | ||
146 | int err; | ||
147 | int i_mode, i_nlink, i_blksize; | ||
148 | unsigned long long i_size; | ||
149 | unsigned long long i_ino; | ||
150 | unsigned long long i_blocks; | ||
151 | |||
152 | err = stat_file(name, &i_ino, &i_mode, &i_nlink, &ino->i_uid, | ||
153 | &ino->i_gid, &i_size, &ino->i_atime, &ino->i_mtime, | ||
154 | &ino->i_ctime, &i_blksize, &i_blocks); | ||
155 | if(err) | ||
156 | return(err); | ||
157 | |||
158 | ino->i_ino = i_ino; | ||
159 | ino->i_mode = i_mode; | ||
160 | ino->i_nlink = i_nlink; | ||
161 | ino->i_size = i_size; | ||
162 | ino->i_blksize = i_blksize; | ||
163 | ino->i_blocks = i_blocks; | ||
164 | if((ino->i_sb->s_dev == ROOT_DEV) && (ino->i_uid == getuid())) | ||
165 | ino->i_uid = 0; | ||
166 | return(0); | ||
167 | } | ||
168 | |||
169 | static char *follow_link(char *link) | ||
170 | { | ||
171 | int len, n; | ||
172 | char *name, *resolved, *end; | ||
173 | |||
174 | len = 64; | ||
175 | while(1){ | ||
176 | n = -ENOMEM; | ||
177 | name = kmalloc(len, GFP_KERNEL); | ||
178 | if(name == NULL) | ||
179 | goto out; | ||
180 | |||
181 | n = do_readlink(link, name, len); | ||
182 | if(n < len) | ||
183 | break; | ||
184 | len *= 2; | ||
185 | kfree(name); | ||
186 | } | ||
187 | if(n < 0) | ||
188 | goto out_free; | ||
189 | |||
190 | if(*name == '/') | ||
191 | return(name); | ||
192 | |||
193 | end = strrchr(link, '/'); | ||
194 | if(end == NULL) | ||
195 | return(name); | ||
196 | |||
197 | *(end + 1) = '\0'; | ||
198 | len = strlen(link) + strlen(name) + 1; | ||
199 | |||
200 | resolved = kmalloc(len, GFP_KERNEL); | ||
201 | if(resolved == NULL){ | ||
202 | n = -ENOMEM; | ||
203 | goto out_free; | ||
204 | } | ||
205 | |||
206 | sprintf(resolved, "%s%s", link, name); | ||
207 | kfree(name); | ||
208 | kfree(link); | ||
209 | return(resolved); | ||
210 | |||
211 | out_free: | ||
212 | kfree(name); | ||
213 | out: | ||
214 | return(ERR_PTR(n)); | ||
215 | } | ||
216 | |||
217 | static int read_inode(struct inode *ino) | ||
218 | { | ||
219 | char *name; | ||
220 | int err = 0; | ||
221 | |||
222 | /* Unfortunately, we are called from iget() when we don't have a dentry | ||
223 | * allocated yet. | ||
224 | */ | ||
225 | if(list_empty(&ino->i_dentry)) | ||
226 | goto out; | ||
227 | |||
228 | err = -ENOMEM; | ||
229 | name = inode_name(ino, 0); | ||
230 | if(name == NULL) | ||
231 | goto out; | ||
232 | |||
233 | if(file_type(name, NULL, NULL) == OS_TYPE_SYMLINK){ | ||
234 | name = follow_link(name); | ||
235 | if(IS_ERR(name)){ | ||
236 | err = PTR_ERR(name); | ||
237 | goto out; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | err = read_name(ino, name); | ||
242 | kfree(name); | ||
243 | out: | ||
244 | return(err); | ||
245 | } | ||
246 | |||
247 | int hostfs_statfs(struct super_block *sb, struct kstatfs *sf) | ||
248 | { | ||
249 | /* do_statfs uses struct statfs64 internally, but the linux kernel | ||
250 | * struct statfs still has 32-bit versions for most of these fields, | ||
251 | * so we convert them here | ||
252 | */ | ||
253 | int err; | ||
254 | long long f_blocks; | ||
255 | long long f_bfree; | ||
256 | long long f_bavail; | ||
257 | long long f_files; | ||
258 | long long f_ffree; | ||
259 | |||
260 | err = do_statfs(HOSTFS_I(sb->s_root->d_inode)->host_filename, | ||
261 | &sf->f_bsize, &f_blocks, &f_bfree, &f_bavail, &f_files, | ||
262 | &f_ffree, &sf->f_fsid, sizeof(sf->f_fsid), | ||
263 | &sf->f_namelen, sf->f_spare); | ||
264 | if(err) return(err); | ||
265 | sf->f_blocks = f_blocks; | ||
266 | sf->f_bfree = f_bfree; | ||
267 | sf->f_bavail = f_bavail; | ||
268 | sf->f_files = f_files; | ||
269 | sf->f_ffree = f_ffree; | ||
270 | sf->f_type = HOSTFS_SUPER_MAGIC; | ||
271 | return(0); | ||
272 | } | ||
273 | |||
274 | static struct inode *hostfs_alloc_inode(struct super_block *sb) | ||
275 | { | ||
276 | struct hostfs_inode_info *hi; | ||
277 | |||
278 | hi = kmalloc(sizeof(*hi), GFP_KERNEL); | ||
279 | if(hi == NULL) | ||
280 | return(NULL); | ||
281 | |||
282 | *hi = ((struct hostfs_inode_info) { .host_filename = NULL, | ||
283 | .fd = -1, | ||
284 | .mode = 0 }); | ||
285 | inode_init_once(&hi->vfs_inode); | ||
286 | return(&hi->vfs_inode); | ||
287 | } | ||
288 | |||
289 | static void hostfs_delete_inode(struct inode *inode) | ||
290 | { | ||
291 | if(HOSTFS_I(inode)->fd != -1) { | ||
292 | close_file(&HOSTFS_I(inode)->fd); | ||
293 | HOSTFS_I(inode)->fd = -1; | ||
294 | } | ||
295 | clear_inode(inode); | ||
296 | } | ||
297 | |||
298 | static void hostfs_destroy_inode(struct inode *inode) | ||
299 | { | ||
300 | if(HOSTFS_I(inode)->host_filename) | ||
301 | kfree(HOSTFS_I(inode)->host_filename); | ||
302 | |||
303 | /*XXX: This should not happen, probably. The check is here for | ||
304 | * additional safety.*/ | ||
305 | if(HOSTFS_I(inode)->fd != -1) { | ||
306 | close_file(&HOSTFS_I(inode)->fd); | ||
307 | printk(KERN_DEBUG "Closing host fd in .destroy_inode\n"); | ||
308 | } | ||
309 | |||
310 | kfree(HOSTFS_I(inode)); | ||
311 | } | ||
312 | |||
313 | static void hostfs_read_inode(struct inode *inode) | ||
314 | { | ||
315 | read_inode(inode); | ||
316 | } | ||
317 | |||
318 | static struct super_operations hostfs_sbops = { | ||
319 | .alloc_inode = hostfs_alloc_inode, | ||
320 | .drop_inode = generic_delete_inode, | ||
321 | .delete_inode = hostfs_delete_inode, | ||
322 | .destroy_inode = hostfs_destroy_inode, | ||
323 | .read_inode = hostfs_read_inode, | ||
324 | .statfs = hostfs_statfs, | ||
325 | }; | ||
326 | |||
327 | int hostfs_readdir(struct file *file, void *ent, filldir_t filldir) | ||
328 | { | ||
329 | void *dir; | ||
330 | char *name; | ||
331 | unsigned long long next, ino; | ||
332 | int error, len; | ||
333 | |||
334 | name = dentry_name(file->f_dentry, 0); | ||
335 | if(name == NULL) return(-ENOMEM); | ||
336 | dir = open_dir(name, &error); | ||
337 | kfree(name); | ||
338 | if(dir == NULL) return(-error); | ||
339 | next = file->f_pos; | ||
340 | while((name = read_dir(dir, &next, &ino, &len)) != NULL){ | ||
341 | error = (*filldir)(ent, name, len, file->f_pos, | ||
342 | ino, DT_UNKNOWN); | ||
343 | if(error) break; | ||
344 | file->f_pos = next; | ||
345 | } | ||
346 | close_dir(dir); | ||
347 | return(0); | ||
348 | } | ||
349 | |||
350 | int hostfs_file_open(struct inode *ino, struct file *file) | ||
351 | { | ||
352 | char *name; | ||
353 | int mode = 0, r = 0, w = 0, fd; | ||
354 | |||
355 | mode = file->f_mode & (FMODE_READ | FMODE_WRITE); | ||
356 | if((mode & HOSTFS_I(ino)->mode) == mode) | ||
357 | return(0); | ||
358 | |||
359 | /* The file may already have been opened, but with the wrong access, | ||
360 | * so this resets things and reopens the file with the new access. | ||
361 | */ | ||
362 | if(HOSTFS_I(ino)->fd != -1){ | ||
363 | close_file(&HOSTFS_I(ino)->fd); | ||
364 | HOSTFS_I(ino)->fd = -1; | ||
365 | } | ||
366 | |||
367 | HOSTFS_I(ino)->mode |= mode; | ||
368 | if(HOSTFS_I(ino)->mode & FMODE_READ) | ||
369 | r = 1; | ||
370 | if(HOSTFS_I(ino)->mode & FMODE_WRITE) | ||
371 | w = 1; | ||
372 | if(w) | ||
373 | r = 1; | ||
374 | |||
375 | name = dentry_name(file->f_dentry, 0); | ||
376 | if(name == NULL) | ||
377 | return(-ENOMEM); | ||
378 | |||
379 | fd = open_file(name, r, w, append); | ||
380 | kfree(name); | ||
381 | if(fd < 0) return(fd); | ||
382 | FILE_HOSTFS_I(file)->fd = fd; | ||
383 | |||
384 | return(0); | ||
385 | } | ||
386 | |||
387 | int hostfs_fsync(struct file *file, struct dentry *dentry, int datasync) | ||
388 | { | ||
389 | return(0); | ||
390 | } | ||
391 | |||
392 | static struct file_operations hostfs_file_fops = { | ||
393 | .llseek = generic_file_llseek, | ||
394 | .read = generic_file_read, | ||
395 | .sendfile = generic_file_sendfile, | ||
396 | .aio_read = generic_file_aio_read, | ||
397 | .aio_write = generic_file_aio_write, | ||
398 | .readv = generic_file_readv, | ||
399 | .writev = generic_file_writev, | ||
400 | .write = generic_file_write, | ||
401 | .mmap = generic_file_mmap, | ||
402 | .open = hostfs_file_open, | ||
403 | .release = NULL, | ||
404 | .fsync = hostfs_fsync, | ||
405 | }; | ||
406 | |||
407 | static struct file_operations hostfs_dir_fops = { | ||
408 | .llseek = generic_file_llseek, | ||
409 | .readdir = hostfs_readdir, | ||
410 | .read = generic_read_dir, | ||
411 | }; | ||
412 | |||
413 | int hostfs_writepage(struct page *page, struct writeback_control *wbc) | ||
414 | { | ||
415 | struct address_space *mapping = page->mapping; | ||
416 | struct inode *inode = mapping->host; | ||
417 | char *buffer; | ||
418 | unsigned long long base; | ||
419 | int count = PAGE_CACHE_SIZE; | ||
420 | int end_index = inode->i_size >> PAGE_CACHE_SHIFT; | ||
421 | int err; | ||
422 | |||
423 | if (page->index >= end_index) | ||
424 | count = inode->i_size & (PAGE_CACHE_SIZE-1); | ||
425 | |||
426 | buffer = kmap(page); | ||
427 | base = ((unsigned long long) page->index) << PAGE_CACHE_SHIFT; | ||
428 | |||
429 | err = write_file(HOSTFS_I(inode)->fd, &base, buffer, count); | ||
430 | if(err != count){ | ||
431 | ClearPageUptodate(page); | ||
432 | goto out; | ||
433 | } | ||
434 | |||
435 | if (base > inode->i_size) | ||
436 | inode->i_size = base; | ||
437 | |||
438 | if (PageError(page)) | ||
439 | ClearPageError(page); | ||
440 | err = 0; | ||
441 | |||
442 | out: | ||
443 | kunmap(page); | ||
444 | |||
445 | unlock_page(page); | ||
446 | return err; | ||
447 | } | ||
448 | |||
449 | int hostfs_readpage(struct file *file, struct page *page) | ||
450 | { | ||
451 | char *buffer; | ||
452 | long long start; | ||
453 | int err = 0; | ||
454 | |||
455 | start = (long long) page->index << PAGE_CACHE_SHIFT; | ||
456 | buffer = kmap(page); | ||
457 | err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer, | ||
458 | PAGE_CACHE_SIZE); | ||
459 | if(err < 0) goto out; | ||
460 | |||
461 | memset(&buffer[err], 0, PAGE_CACHE_SIZE - err); | ||
462 | |||
463 | flush_dcache_page(page); | ||
464 | SetPageUptodate(page); | ||
465 | if (PageError(page)) ClearPageError(page); | ||
466 | err = 0; | ||
467 | out: | ||
468 | kunmap(page); | ||
469 | unlock_page(page); | ||
470 | return(err); | ||
471 | } | ||
472 | |||
473 | int hostfs_prepare_write(struct file *file, struct page *page, | ||
474 | unsigned int from, unsigned int to) | ||
475 | { | ||
476 | char *buffer; | ||
477 | long long start, tmp; | ||
478 | int err; | ||
479 | |||
480 | start = (long long) page->index << PAGE_CACHE_SHIFT; | ||
481 | buffer = kmap(page); | ||
482 | if(from != 0){ | ||
483 | tmp = start; | ||
484 | err = read_file(FILE_HOSTFS_I(file)->fd, &tmp, buffer, | ||
485 | from); | ||
486 | if(err < 0) goto out; | ||
487 | } | ||
488 | if(to != PAGE_CACHE_SIZE){ | ||
489 | start += to; | ||
490 | err = read_file(FILE_HOSTFS_I(file)->fd, &start, buffer + to, | ||
491 | PAGE_CACHE_SIZE - to); | ||
492 | if(err < 0) goto out; | ||
493 | } | ||
494 | err = 0; | ||
495 | out: | ||
496 | kunmap(page); | ||
497 | return(err); | ||
498 | } | ||
499 | |||
500 | int hostfs_commit_write(struct file *file, struct page *page, unsigned from, | ||
501 | unsigned to) | ||
502 | { | ||
503 | struct address_space *mapping = page->mapping; | ||
504 | struct inode *inode = mapping->host; | ||
505 | char *buffer; | ||
506 | long long start; | ||
507 | int err = 0; | ||
508 | |||
509 | start = (long long) (page->index << PAGE_CACHE_SHIFT) + from; | ||
510 | buffer = kmap(page); | ||
511 | err = write_file(FILE_HOSTFS_I(file)->fd, &start, buffer + from, | ||
512 | to - from); | ||
513 | if(err > 0) err = 0; | ||
514 | if(!err && (start > inode->i_size)) | ||
515 | inode->i_size = start; | ||
516 | |||
517 | kunmap(page); | ||
518 | return(err); | ||
519 | } | ||
520 | |||
521 | static struct address_space_operations hostfs_aops = { | ||
522 | .writepage = hostfs_writepage, | ||
523 | .readpage = hostfs_readpage, | ||
524 | /* .set_page_dirty = __set_page_dirty_nobuffers, */ | ||
525 | .prepare_write = hostfs_prepare_write, | ||
526 | .commit_write = hostfs_commit_write | ||
527 | }; | ||
528 | |||
529 | static int init_inode(struct inode *inode, struct dentry *dentry) | ||
530 | { | ||
531 | char *name; | ||
532 | int type, err = -ENOMEM; | ||
533 | int maj, min; | ||
534 | dev_t rdev = 0; | ||
535 | |||
536 | if(dentry){ | ||
537 | name = dentry_name(dentry, 0); | ||
538 | if(name == NULL) | ||
539 | goto out; | ||
540 | type = file_type(name, &maj, &min); | ||
541 | /*Reencode maj and min with the kernel encoding.*/ | ||
542 | rdev = MKDEV(maj, min); | ||
543 | kfree(name); | ||
544 | } | ||
545 | else type = OS_TYPE_DIR; | ||
546 | |||
547 | err = 0; | ||
548 | if(type == OS_TYPE_SYMLINK) | ||
549 | inode->i_op = &page_symlink_inode_operations; | ||
550 | else if(type == OS_TYPE_DIR) | ||
551 | inode->i_op = &hostfs_dir_iops; | ||
552 | else inode->i_op = &hostfs_iops; | ||
553 | |||
554 | if(type == OS_TYPE_DIR) inode->i_fop = &hostfs_dir_fops; | ||
555 | else inode->i_fop = &hostfs_file_fops; | ||
556 | |||
557 | if(type == OS_TYPE_SYMLINK) | ||
558 | inode->i_mapping->a_ops = &hostfs_link_aops; | ||
559 | else inode->i_mapping->a_ops = &hostfs_aops; | ||
560 | |||
561 | switch (type) { | ||
562 | case OS_TYPE_CHARDEV: | ||
563 | init_special_inode(inode, S_IFCHR, rdev); | ||
564 | break; | ||
565 | case OS_TYPE_BLOCKDEV: | ||
566 | init_special_inode(inode, S_IFBLK, rdev); | ||
567 | break; | ||
568 | case OS_TYPE_FIFO: | ||
569 | init_special_inode(inode, S_IFIFO, 0); | ||
570 | break; | ||
571 | case OS_TYPE_SOCK: | ||
572 | init_special_inode(inode, S_IFSOCK, 0); | ||
573 | break; | ||
574 | } | ||
575 | out: | ||
576 | return(err); | ||
577 | } | ||
578 | |||
579 | int hostfs_create(struct inode *dir, struct dentry *dentry, int mode, | ||
580 | struct nameidata *nd) | ||
581 | { | ||
582 | struct inode *inode; | ||
583 | char *name; | ||
584 | int error, fd; | ||
585 | |||
586 | error = -ENOMEM; | ||
587 | inode = iget(dir->i_sb, 0); | ||
588 | if(inode == NULL) goto out; | ||
589 | |||
590 | error = init_inode(inode, dentry); | ||
591 | if(error) | ||
592 | goto out_put; | ||
593 | |||
594 | error = -ENOMEM; | ||
595 | name = dentry_name(dentry, 0); | ||
596 | if(name == NULL) | ||
597 | goto out_put; | ||
598 | |||
599 | fd = file_create(name, | ||
600 | mode & S_IRUSR, mode & S_IWUSR, mode & S_IXUSR, | ||
601 | mode & S_IRGRP, mode & S_IWGRP, mode & S_IXGRP, | ||
602 | mode & S_IROTH, mode & S_IWOTH, mode & S_IXOTH); | ||
603 | if(fd < 0) | ||
604 | error = fd; | ||
605 | else error = read_name(inode, name); | ||
606 | |||
607 | kfree(name); | ||
608 | if(error) | ||
609 | goto out_put; | ||
610 | |||
611 | HOSTFS_I(inode)->fd = fd; | ||
612 | HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; | ||
613 | d_instantiate(dentry, inode); | ||
614 | return(0); | ||
615 | |||
616 | out_put: | ||
617 | iput(inode); | ||
618 | out: | ||
619 | return(error); | ||
620 | } | ||
621 | |||
622 | struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, | ||
623 | struct nameidata *nd) | ||
624 | { | ||
625 | struct inode *inode; | ||
626 | char *name; | ||
627 | int err; | ||
628 | |||
629 | err = -ENOMEM; | ||
630 | inode = iget(ino->i_sb, 0); | ||
631 | if(inode == NULL) | ||
632 | goto out; | ||
633 | |||
634 | err = init_inode(inode, dentry); | ||
635 | if(err) | ||
636 | goto out_put; | ||
637 | |||
638 | err = -ENOMEM; | ||
639 | name = dentry_name(dentry, 0); | ||
640 | if(name == NULL) | ||
641 | goto out_put; | ||
642 | |||
643 | err = read_name(inode, name); | ||
644 | kfree(name); | ||
645 | if(err == -ENOENT){ | ||
646 | iput(inode); | ||
647 | inode = NULL; | ||
648 | } | ||
649 | else if(err) | ||
650 | goto out_put; | ||
651 | |||
652 | d_add(dentry, inode); | ||
653 | dentry->d_op = &hostfs_dentry_ops; | ||
654 | return(NULL); | ||
655 | |||
656 | out_put: | ||
657 | iput(inode); | ||
658 | out: | ||
659 | return(ERR_PTR(err)); | ||
660 | } | ||
661 | |||
662 | static char *inode_dentry_name(struct inode *ino, struct dentry *dentry) | ||
663 | { | ||
664 | char *file; | ||
665 | int len; | ||
666 | |||
667 | file = inode_name(ino, dentry->d_name.len + 1); | ||
668 | if(file == NULL) return(NULL); | ||
669 | strcat(file, "/"); | ||
670 | len = strlen(file); | ||
671 | strncat(file, dentry->d_name.name, dentry->d_name.len); | ||
672 | file[len + dentry->d_name.len] = '\0'; | ||
673 | return(file); | ||
674 | } | ||
675 | |||
676 | int hostfs_link(struct dentry *to, struct inode *ino, struct dentry *from) | ||
677 | { | ||
678 | char *from_name, *to_name; | ||
679 | int err; | ||
680 | |||
681 | if((from_name = inode_dentry_name(ino, from)) == NULL) | ||
682 | return(-ENOMEM); | ||
683 | to_name = dentry_name(to, 0); | ||
684 | if(to_name == NULL){ | ||
685 | kfree(from_name); | ||
686 | return(-ENOMEM); | ||
687 | } | ||
688 | err = link_file(to_name, from_name); | ||
689 | kfree(from_name); | ||
690 | kfree(to_name); | ||
691 | return(err); | ||
692 | } | ||
693 | |||
694 | int hostfs_unlink(struct inode *ino, struct dentry *dentry) | ||
695 | { | ||
696 | char *file; | ||
697 | int err; | ||
698 | |||
699 | if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); | ||
700 | if(append) | ||
701 | return(-EPERM); | ||
702 | |||
703 | err = unlink_file(file); | ||
704 | kfree(file); | ||
705 | return(err); | ||
706 | } | ||
707 | |||
708 | int hostfs_symlink(struct inode *ino, struct dentry *dentry, const char *to) | ||
709 | { | ||
710 | char *file; | ||
711 | int err; | ||
712 | |||
713 | if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); | ||
714 | err = make_symlink(file, to); | ||
715 | kfree(file); | ||
716 | return(err); | ||
717 | } | ||
718 | |||
719 | int hostfs_mkdir(struct inode *ino, struct dentry *dentry, int mode) | ||
720 | { | ||
721 | char *file; | ||
722 | int err; | ||
723 | |||
724 | if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); | ||
725 | err = do_mkdir(file, mode); | ||
726 | kfree(file); | ||
727 | return(err); | ||
728 | } | ||
729 | |||
730 | int hostfs_rmdir(struct inode *ino, struct dentry *dentry) | ||
731 | { | ||
732 | char *file; | ||
733 | int err; | ||
734 | |||
735 | if((file = inode_dentry_name(ino, dentry)) == NULL) return(-ENOMEM); | ||
736 | err = do_rmdir(file); | ||
737 | kfree(file); | ||
738 | return(err); | ||
739 | } | ||
740 | |||
741 | int hostfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) | ||
742 | { | ||
743 | struct inode *inode; | ||
744 | char *name; | ||
745 | int err = -ENOMEM; | ||
746 | |||
747 | inode = iget(dir->i_sb, 0); | ||
748 | if(inode == NULL) | ||
749 | goto out; | ||
750 | |||
751 | err = init_inode(inode, dentry); | ||
752 | if(err) | ||
753 | goto out_put; | ||
754 | |||
755 | err = -ENOMEM; | ||
756 | name = dentry_name(dentry, 0); | ||
757 | if(name == NULL) | ||
758 | goto out_put; | ||
759 | |||
760 | init_special_inode(inode, mode, dev); | ||
761 | err = do_mknod(name, mode, dev); | ||
762 | if(err) | ||
763 | goto out_free; | ||
764 | |||
765 | err = read_name(inode, name); | ||
766 | kfree(name); | ||
767 | if(err) | ||
768 | goto out_put; | ||
769 | |||
770 | d_instantiate(dentry, inode); | ||
771 | return(0); | ||
772 | |||
773 | out_free: | ||
774 | kfree(name); | ||
775 | out_put: | ||
776 | iput(inode); | ||
777 | out: | ||
778 | return(err); | ||
779 | } | ||
780 | |||
781 | int hostfs_rename(struct inode *from_ino, struct dentry *from, | ||
782 | struct inode *to_ino, struct dentry *to) | ||
783 | { | ||
784 | char *from_name, *to_name; | ||
785 | int err; | ||
786 | |||
787 | if((from_name = inode_dentry_name(from_ino, from)) == NULL) | ||
788 | return(-ENOMEM); | ||
789 | if((to_name = inode_dentry_name(to_ino, to)) == NULL){ | ||
790 | kfree(from_name); | ||
791 | return(-ENOMEM); | ||
792 | } | ||
793 | err = rename_file(from_name, to_name); | ||
794 | kfree(from_name); | ||
795 | kfree(to_name); | ||
796 | return(err); | ||
797 | } | ||
798 | |||
799 | void hostfs_truncate(struct inode *ino) | ||
800 | { | ||
801 | not_implemented(); | ||
802 | } | ||
803 | |||
804 | int hostfs_permission(struct inode *ino, int desired, struct nameidata *nd) | ||
805 | { | ||
806 | char *name; | ||
807 | int r = 0, w = 0, x = 0, err; | ||
808 | |||
809 | if (desired & MAY_READ) r = 1; | ||
810 | if (desired & MAY_WRITE) w = 1; | ||
811 | if (desired & MAY_EXEC) x = 1; | ||
812 | name = inode_name(ino, 0); | ||
813 | if (name == NULL) return(-ENOMEM); | ||
814 | |||
815 | if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) || | ||
816 | S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode)) | ||
817 | err = 0; | ||
818 | else | ||
819 | err = access_file(name, r, w, x); | ||
820 | kfree(name); | ||
821 | if(!err) | ||
822 | err = generic_permission(ino, desired, NULL); | ||
823 | return err; | ||
824 | } | ||
825 | |||
826 | int hostfs_setattr(struct dentry *dentry, struct iattr *attr) | ||
827 | { | ||
828 | struct hostfs_iattr attrs; | ||
829 | char *name; | ||
830 | int err; | ||
831 | |||
832 | err = inode_change_ok(dentry->d_inode, attr); | ||
833 | if (err) | ||
834 | return err; | ||
835 | |||
836 | if(append) | ||
837 | attr->ia_valid &= ~ATTR_SIZE; | ||
838 | |||
839 | attrs.ia_valid = 0; | ||
840 | if(attr->ia_valid & ATTR_MODE){ | ||
841 | attrs.ia_valid |= HOSTFS_ATTR_MODE; | ||
842 | attrs.ia_mode = attr->ia_mode; | ||
843 | } | ||
844 | if(attr->ia_valid & ATTR_UID){ | ||
845 | if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && | ||
846 | (attr->ia_uid == 0)) | ||
847 | attr->ia_uid = getuid(); | ||
848 | attrs.ia_valid |= HOSTFS_ATTR_UID; | ||
849 | attrs.ia_uid = attr->ia_uid; | ||
850 | } | ||
851 | if(attr->ia_valid & ATTR_GID){ | ||
852 | if((dentry->d_inode->i_sb->s_dev == ROOT_DEV) && | ||
853 | (attr->ia_gid == 0)) | ||
854 | attr->ia_gid = getgid(); | ||
855 | attrs.ia_valid |= HOSTFS_ATTR_GID; | ||
856 | attrs.ia_gid = attr->ia_gid; | ||
857 | } | ||
858 | if(attr->ia_valid & ATTR_SIZE){ | ||
859 | attrs.ia_valid |= HOSTFS_ATTR_SIZE; | ||
860 | attrs.ia_size = attr->ia_size; | ||
861 | } | ||
862 | if(attr->ia_valid & ATTR_ATIME){ | ||
863 | attrs.ia_valid |= HOSTFS_ATTR_ATIME; | ||
864 | attrs.ia_atime = attr->ia_atime; | ||
865 | } | ||
866 | if(attr->ia_valid & ATTR_MTIME){ | ||
867 | attrs.ia_valid |= HOSTFS_ATTR_MTIME; | ||
868 | attrs.ia_mtime = attr->ia_mtime; | ||
869 | } | ||
870 | if(attr->ia_valid & ATTR_CTIME){ | ||
871 | attrs.ia_valid |= HOSTFS_ATTR_CTIME; | ||
872 | attrs.ia_ctime = attr->ia_ctime; | ||
873 | } | ||
874 | if(attr->ia_valid & ATTR_ATIME_SET){ | ||
875 | attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; | ||
876 | } | ||
877 | if(attr->ia_valid & ATTR_MTIME_SET){ | ||
878 | attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; | ||
879 | } | ||
880 | name = dentry_name(dentry, 0); | ||
881 | if(name == NULL) return(-ENOMEM); | ||
882 | err = set_attr(name, &attrs); | ||
883 | kfree(name); | ||
884 | if(err) | ||
885 | return(err); | ||
886 | |||
887 | return(inode_setattr(dentry->d_inode, attr)); | ||
888 | } | ||
889 | |||
890 | int hostfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | ||
891 | struct kstat *stat) | ||
892 | { | ||
893 | generic_fillattr(dentry->d_inode, stat); | ||
894 | return(0); | ||
895 | } | ||
896 | |||
897 | static struct inode_operations hostfs_iops = { | ||
898 | .create = hostfs_create, | ||
899 | .link = hostfs_link, | ||
900 | .unlink = hostfs_unlink, | ||
901 | .symlink = hostfs_symlink, | ||
902 | .mkdir = hostfs_mkdir, | ||
903 | .rmdir = hostfs_rmdir, | ||
904 | .mknod = hostfs_mknod, | ||
905 | .rename = hostfs_rename, | ||
906 | .truncate = hostfs_truncate, | ||
907 | .permission = hostfs_permission, | ||
908 | .setattr = hostfs_setattr, | ||
909 | .getattr = hostfs_getattr, | ||
910 | }; | ||
911 | |||
912 | static struct inode_operations hostfs_dir_iops = { | ||
913 | .create = hostfs_create, | ||
914 | .lookup = hostfs_lookup, | ||
915 | .link = hostfs_link, | ||
916 | .unlink = hostfs_unlink, | ||
917 | .symlink = hostfs_symlink, | ||
918 | .mkdir = hostfs_mkdir, | ||
919 | .rmdir = hostfs_rmdir, | ||
920 | .mknod = hostfs_mknod, | ||
921 | .rename = hostfs_rename, | ||
922 | .truncate = hostfs_truncate, | ||
923 | .permission = hostfs_permission, | ||
924 | .setattr = hostfs_setattr, | ||
925 | .getattr = hostfs_getattr, | ||
926 | }; | ||
927 | |||
928 | int hostfs_link_readpage(struct file *file, struct page *page) | ||
929 | { | ||
930 | char *buffer, *name; | ||
931 | long long start; | ||
932 | int err; | ||
933 | |||
934 | start = page->index << PAGE_CACHE_SHIFT; | ||
935 | buffer = kmap(page); | ||
936 | name = inode_name(page->mapping->host, 0); | ||
937 | if(name == NULL) return(-ENOMEM); | ||
938 | err = do_readlink(name, buffer, PAGE_CACHE_SIZE); | ||
939 | kfree(name); | ||
940 | if(err == PAGE_CACHE_SIZE) | ||
941 | err = -E2BIG; | ||
942 | else if(err > 0){ | ||
943 | flush_dcache_page(page); | ||
944 | SetPageUptodate(page); | ||
945 | if (PageError(page)) ClearPageError(page); | ||
946 | err = 0; | ||
947 | } | ||
948 | kunmap(page); | ||
949 | unlock_page(page); | ||
950 | return(err); | ||
951 | } | ||
952 | |||
953 | static struct address_space_operations hostfs_link_aops = { | ||
954 | .readpage = hostfs_link_readpage, | ||
955 | }; | ||
956 | |||
957 | static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) | ||
958 | { | ||
959 | struct inode *root_inode; | ||
960 | char *name, *data = d; | ||
961 | int err; | ||
962 | |||
963 | sb->s_blocksize = 1024; | ||
964 | sb->s_blocksize_bits = 10; | ||
965 | sb->s_magic = HOSTFS_SUPER_MAGIC; | ||
966 | sb->s_op = &hostfs_sbops; | ||
967 | |||
968 | if((data == NULL) || (*data == '\0')) | ||
969 | data = root_ino; | ||
970 | |||
971 | err = -ENOMEM; | ||
972 | name = kmalloc(strlen(data) + 1, GFP_KERNEL); | ||
973 | if(name == NULL) | ||
974 | goto out; | ||
975 | |||
976 | strcpy(name, data); | ||
977 | |||
978 | root_inode = iget(sb, 0); | ||
979 | if(root_inode == NULL) | ||
980 | goto out_free; | ||
981 | |||
982 | err = init_inode(root_inode, NULL); | ||
983 | if(err) | ||
984 | goto out_put; | ||
985 | |||
986 | HOSTFS_I(root_inode)->host_filename = name; | ||
987 | |||
988 | err = -ENOMEM; | ||
989 | sb->s_root = d_alloc_root(root_inode); | ||
990 | if(sb->s_root == NULL) | ||
991 | goto out_put; | ||
992 | |||
993 | err = read_inode(root_inode); | ||
994 | if(err) | ||
995 | goto out_put; | ||
996 | |||
997 | return(0); | ||
998 | |||
999 | out_put: | ||
1000 | iput(root_inode); | ||
1001 | out_free: | ||
1002 | kfree(name); | ||
1003 | out: | ||
1004 | return(err); | ||
1005 | } | ||
1006 | |||
1007 | static struct super_block *hostfs_read_sb(struct file_system_type *type, | ||
1008 | int flags, const char *dev_name, | ||
1009 | void *data) | ||
1010 | { | ||
1011 | return(get_sb_nodev(type, flags, data, hostfs_fill_sb_common)); | ||
1012 | } | ||
1013 | |||
1014 | static struct file_system_type hostfs_type = { | ||
1015 | .owner = THIS_MODULE, | ||
1016 | .name = "hostfs", | ||
1017 | .get_sb = hostfs_read_sb, | ||
1018 | .kill_sb = kill_anon_super, | ||
1019 | .fs_flags = 0, | ||
1020 | }; | ||
1021 | |||
1022 | static int __init init_hostfs(void) | ||
1023 | { | ||
1024 | return(register_filesystem(&hostfs_type)); | ||
1025 | } | ||
1026 | |||
1027 | static void __exit exit_hostfs(void) | ||
1028 | { | ||
1029 | unregister_filesystem(&hostfs_type); | ||
1030 | } | ||
1031 | |||
1032 | module_init(init_hostfs) | ||
1033 | module_exit(exit_hostfs) | ||
1034 | MODULE_LICENSE("GPL"); | ||
1035 | |||
1036 | /* | ||
1037 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
1038 | * Emacs will notice this stuff at the end of the file and automatically | ||
1039 | * adjust the settings for this buffer only. This must remain at the end | ||
1040 | * of the file. | ||
1041 | * --------------------------------------------------------------------------- | ||
1042 | * Local variables: | ||
1043 | * c-file-style: "linux" | ||
1044 | * End: | ||
1045 | */ | ||