1 | /* |
2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.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/fs.h> |
10 | #include <linux/magic.h> |
11 | #include <linux/module.h> |
12 | #include <linux/mm.h> |
13 | #include <linux/pagemap.h> |
14 | #include <linux/statfs.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/seq_file.h> |
17 | #include <linux/writeback.h> |
18 | #include <linux/mount.h> |
19 | #include <linux/namei.h> |
20 | #include "hostfs.h" |
21 | #include <init.h> |
22 | #include <kern.h> |
23 | |
24 | struct hostfs_inode_info { |
25 | int fd; |
26 | fmode_t mode; |
27 | struct inode vfs_inode; |
28 | struct mutex open_mutex; |
29 | dev_t dev; |
30 | }; |
31 | |
32 | static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) |
33 | { |
34 | return list_entry(inode, struct hostfs_inode_info, vfs_inode); |
35 | } |
36 | |
37 | #define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file)) |
38 | |
39 | static struct kmem_cache *hostfs_inode_cache; |
40 | |
41 | /* Changed in hostfs_args before the kernel starts running */ |
42 | static char *root_ino = "" ; |
43 | static int append = 0; |
44 | |
45 | static const struct inode_operations hostfs_iops; |
46 | static const struct inode_operations hostfs_dir_iops; |
47 | static const struct inode_operations hostfs_link_iops; |
48 | |
49 | #ifndef MODULE |
50 | static int __init hostfs_args(char *options, int *add) |
51 | { |
52 | char *ptr; |
53 | |
54 | ptr = strchr(options, ','); |
55 | if (ptr != NULL) |
56 | *ptr++ = '\0'; |
57 | if (*options != '\0') |
58 | root_ino = options; |
59 | |
60 | options = ptr; |
61 | while (options) { |
62 | ptr = strchr(options, ','); |
63 | if (ptr != NULL) |
64 | *ptr++ = '\0'; |
65 | if (*options != '\0') { |
66 | if (!strcmp(options, "append" )) |
67 | append = 1; |
68 | else printf("hostfs_args - unsupported option - %s\n" , |
69 | options); |
70 | } |
71 | options = ptr; |
72 | } |
73 | return 0; |
74 | } |
75 | |
76 | __uml_setup("hostfs=" , hostfs_args, |
77 | "hostfs=<root dir>,<flags>,...\n" |
78 | " This is used to set hostfs parameters. The root directory argument\n" |
79 | " is used to confine all hostfs mounts to within the specified directory\n" |
80 | " tree on the host. If this isn't specified, then a user inside UML can\n" |
81 | " mount anything on the host that's accessible to the user that's running\n" |
82 | " it.\n" |
83 | " The only flag currently supported is 'append', which specifies that all\n" |
84 | " files opened by hostfs will be opened in append mode.\n\n" |
85 | ); |
86 | #endif |
87 | |
88 | static char *__dentry_name(struct dentry *dentry, char *name) |
89 | { |
90 | char *p = dentry_path_raw(dentry, name, PATH_MAX); |
91 | char *root; |
92 | size_t len; |
93 | |
94 | root = dentry->d_sb->s_fs_info; |
95 | len = strlen(root); |
96 | if (IS_ERR(ptr: p)) { |
97 | __putname(name); |
98 | return NULL; |
99 | } |
100 | |
101 | /* |
102 | * This function relies on the fact that dentry_path_raw() will place |
103 | * the path name at the end of the provided buffer. |
104 | */ |
105 | BUG_ON(p + strlen(p) + 1 != name + PATH_MAX); |
106 | |
107 | strscpy(name, root, PATH_MAX); |
108 | if (len > p - name) { |
109 | __putname(name); |
110 | return NULL; |
111 | } |
112 | |
113 | if (p > name + len) |
114 | strcpy(p: name + len, q: p); |
115 | |
116 | return name; |
117 | } |
118 | |
119 | static char *dentry_name(struct dentry *dentry) |
120 | { |
121 | char *name = __getname(); |
122 | if (!name) |
123 | return NULL; |
124 | |
125 | return __dentry_name(dentry, name); |
126 | } |
127 | |
128 | static char *inode_name(struct inode *ino) |
129 | { |
130 | struct dentry *dentry; |
131 | char *name; |
132 | |
133 | dentry = d_find_alias(ino); |
134 | if (!dentry) |
135 | return NULL; |
136 | |
137 | name = dentry_name(dentry); |
138 | |
139 | dput(dentry); |
140 | |
141 | return name; |
142 | } |
143 | |
144 | static char *follow_link(char *link) |
145 | { |
146 | char *name, *resolved, *end; |
147 | int n; |
148 | |
149 | name = kmalloc(PATH_MAX, GFP_KERNEL); |
150 | if (!name) { |
151 | n = -ENOMEM; |
152 | goto out_free; |
153 | } |
154 | |
155 | n = hostfs_do_readlink(file: link, buf: name, PATH_MAX); |
156 | if (n < 0) |
157 | goto out_free; |
158 | else if (n == PATH_MAX) { |
159 | n = -E2BIG; |
160 | goto out_free; |
161 | } |
162 | |
163 | if (*name == '/') |
164 | return name; |
165 | |
166 | end = strrchr(link, '/'); |
167 | if (end == NULL) |
168 | return name; |
169 | |
170 | *(end + 1) = '\0'; |
171 | |
172 | resolved = kasprintf(GFP_KERNEL, fmt: "%s%s" , link, name); |
173 | if (resolved == NULL) { |
174 | n = -ENOMEM; |
175 | goto out_free; |
176 | } |
177 | |
178 | kfree(objp: name); |
179 | return resolved; |
180 | |
181 | out_free: |
182 | kfree(objp: name); |
183 | return ERR_PTR(error: n); |
184 | } |
185 | |
186 | static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf) |
187 | { |
188 | /* |
189 | * do_statfs uses struct statfs64 internally, but the linux kernel |
190 | * struct statfs still has 32-bit versions for most of these fields, |
191 | * so we convert them here |
192 | */ |
193 | int err; |
194 | long long f_blocks; |
195 | long long f_bfree; |
196 | long long f_bavail; |
197 | long long f_files; |
198 | long long f_ffree; |
199 | |
200 | err = do_statfs(root: dentry->d_sb->s_fs_info, |
201 | bsize_out: &sf->f_bsize, blocks_out: &f_blocks, bfree_out: &f_bfree, bavail_out: &f_bavail, files_out: &f_files, |
202 | ffree_out: &f_ffree, fsid_out: &sf->f_fsid, fsid_size: sizeof(sf->f_fsid), |
203 | namelen_out: &sf->f_namelen); |
204 | if (err) |
205 | return err; |
206 | sf->f_blocks = f_blocks; |
207 | sf->f_bfree = f_bfree; |
208 | sf->f_bavail = f_bavail; |
209 | sf->f_files = f_files; |
210 | sf->f_ffree = f_ffree; |
211 | sf->f_type = HOSTFS_SUPER_MAGIC; |
212 | return 0; |
213 | } |
214 | |
215 | static struct inode *hostfs_alloc_inode(struct super_block *sb) |
216 | { |
217 | struct hostfs_inode_info *hi; |
218 | |
219 | hi = alloc_inode_sb(sb, cache: hostfs_inode_cache, GFP_KERNEL_ACCOUNT); |
220 | if (hi == NULL) |
221 | return NULL; |
222 | hi->fd = -1; |
223 | hi->mode = 0; |
224 | hi->dev = 0; |
225 | inode_init_once(&hi->vfs_inode); |
226 | mutex_init(&hi->open_mutex); |
227 | return &hi->vfs_inode; |
228 | } |
229 | |
230 | static void hostfs_evict_inode(struct inode *inode) |
231 | { |
232 | truncate_inode_pages_final(&inode->i_data); |
233 | clear_inode(inode); |
234 | if (HOSTFS_I(inode)->fd != -1) { |
235 | close_file(stream: &HOSTFS_I(inode)->fd); |
236 | HOSTFS_I(inode)->fd = -1; |
237 | HOSTFS_I(inode)->dev = 0; |
238 | } |
239 | } |
240 | |
241 | static void hostfs_free_inode(struct inode *inode) |
242 | { |
243 | kmem_cache_free(s: hostfs_inode_cache, objp: HOSTFS_I(inode)); |
244 | } |
245 | |
246 | static int hostfs_show_options(struct seq_file *seq, struct dentry *root) |
247 | { |
248 | const char *root_path = root->d_sb->s_fs_info; |
249 | size_t offset = strlen(root_ino) + 1; |
250 | |
251 | if (strlen(root_path) > offset) |
252 | seq_show_option(m: seq, name: root_path + offset, NULL); |
253 | |
254 | if (append) |
255 | seq_puts(m: seq, s: ",append" ); |
256 | |
257 | return 0; |
258 | } |
259 | |
260 | static const struct super_operations hostfs_sbops = { |
261 | .alloc_inode = hostfs_alloc_inode, |
262 | .free_inode = hostfs_free_inode, |
263 | .drop_inode = generic_delete_inode, |
264 | .evict_inode = hostfs_evict_inode, |
265 | .statfs = hostfs_statfs, |
266 | .show_options = hostfs_show_options, |
267 | }; |
268 | |
269 | static int hostfs_readdir(struct file *file, struct dir_context *ctx) |
270 | { |
271 | void *dir; |
272 | char *name; |
273 | unsigned long long next, ino; |
274 | int error, len; |
275 | unsigned int type; |
276 | |
277 | name = dentry_name(dentry: file->f_path.dentry); |
278 | if (name == NULL) |
279 | return -ENOMEM; |
280 | dir = open_dir(path: name, err_out: &error); |
281 | __putname(name); |
282 | if (dir == NULL) |
283 | return -error; |
284 | next = ctx->pos; |
285 | seek_dir(stream: dir, pos: next); |
286 | while ((name = read_dir(stream: dir, pos_out: &next, ino_out: &ino, len_out: &len, type_out: &type)) != NULL) { |
287 | if (!dir_emit(ctx, name, namelen: len, ino, type)) |
288 | break; |
289 | ctx->pos = next; |
290 | } |
291 | close_dir(stream: dir); |
292 | return 0; |
293 | } |
294 | |
295 | static int hostfs_open(struct inode *ino, struct file *file) |
296 | { |
297 | char *name; |
298 | fmode_t mode; |
299 | int err; |
300 | int r, w, fd; |
301 | |
302 | mode = file->f_mode & (FMODE_READ | FMODE_WRITE); |
303 | if ((mode & HOSTFS_I(inode: ino)->mode) == mode) |
304 | return 0; |
305 | |
306 | mode |= HOSTFS_I(inode: ino)->mode; |
307 | |
308 | retry: |
309 | r = w = 0; |
310 | |
311 | if (mode & FMODE_READ) |
312 | r = 1; |
313 | if (mode & FMODE_WRITE) |
314 | r = w = 1; |
315 | |
316 | name = dentry_name(dentry: file_dentry(file)); |
317 | if (name == NULL) |
318 | return -ENOMEM; |
319 | |
320 | fd = open_file(path: name, r, w, append); |
321 | __putname(name); |
322 | if (fd < 0) |
323 | return fd; |
324 | |
325 | mutex_lock(&HOSTFS_I(ino)->open_mutex); |
326 | /* somebody else had handled it first? */ |
327 | if ((mode & HOSTFS_I(inode: ino)->mode) == mode) { |
328 | mutex_unlock(lock: &HOSTFS_I(inode: ino)->open_mutex); |
329 | close_file(stream: &fd); |
330 | return 0; |
331 | } |
332 | if ((mode | HOSTFS_I(inode: ino)->mode) != mode) { |
333 | mode |= HOSTFS_I(inode: ino)->mode; |
334 | mutex_unlock(lock: &HOSTFS_I(inode: ino)->open_mutex); |
335 | close_file(stream: &fd); |
336 | goto retry; |
337 | } |
338 | if (HOSTFS_I(inode: ino)->fd == -1) { |
339 | HOSTFS_I(inode: ino)->fd = fd; |
340 | } else { |
341 | err = replace_file(oldfd: fd, fd: HOSTFS_I(inode: ino)->fd); |
342 | close_file(stream: &fd); |
343 | if (err < 0) { |
344 | mutex_unlock(lock: &HOSTFS_I(inode: ino)->open_mutex); |
345 | return err; |
346 | } |
347 | } |
348 | HOSTFS_I(inode: ino)->mode = mode; |
349 | mutex_unlock(lock: &HOSTFS_I(inode: ino)->open_mutex); |
350 | |
351 | return 0; |
352 | } |
353 | |
354 | static int hostfs_file_release(struct inode *inode, struct file *file) |
355 | { |
356 | filemap_write_and_wait(mapping: inode->i_mapping); |
357 | |
358 | return 0; |
359 | } |
360 | |
361 | static int hostfs_fsync(struct file *file, loff_t start, loff_t end, |
362 | int datasync) |
363 | { |
364 | struct inode *inode = file->f_mapping->host; |
365 | int ret; |
366 | |
367 | ret = file_write_and_wait_range(file, start, end); |
368 | if (ret) |
369 | return ret; |
370 | |
371 | inode_lock(inode); |
372 | ret = fsync_file(fd: HOSTFS_I(inode)->fd, datasync); |
373 | inode_unlock(inode); |
374 | |
375 | return ret; |
376 | } |
377 | |
378 | static const struct file_operations hostfs_file_fops = { |
379 | .llseek = generic_file_llseek, |
380 | .splice_read = filemap_splice_read, |
381 | .splice_write = iter_file_splice_write, |
382 | .read_iter = generic_file_read_iter, |
383 | .write_iter = generic_file_write_iter, |
384 | .mmap = generic_file_mmap, |
385 | .open = hostfs_open, |
386 | .release = hostfs_file_release, |
387 | .fsync = hostfs_fsync, |
388 | }; |
389 | |
390 | static const struct file_operations hostfs_dir_fops = { |
391 | .llseek = generic_file_llseek, |
392 | .iterate_shared = hostfs_readdir, |
393 | .read = generic_read_dir, |
394 | .open = hostfs_open, |
395 | .fsync = hostfs_fsync, |
396 | }; |
397 | |
398 | static int hostfs_writepage(struct page *page, struct writeback_control *wbc) |
399 | { |
400 | struct address_space *mapping = page->mapping; |
401 | struct inode *inode = mapping->host; |
402 | char *buffer; |
403 | loff_t base = page_offset(page); |
404 | int count = PAGE_SIZE; |
405 | int end_index = inode->i_size >> PAGE_SHIFT; |
406 | int err; |
407 | |
408 | if (page->index >= end_index) |
409 | count = inode->i_size & (PAGE_SIZE-1); |
410 | |
411 | buffer = kmap_local_page(page); |
412 | |
413 | err = write_file(fd: HOSTFS_I(inode)->fd, offset: &base, buf: buffer, len: count); |
414 | if (err != count) { |
415 | if (err >= 0) |
416 | err = -EIO; |
417 | mapping_set_error(mapping, error: err); |
418 | goto out; |
419 | } |
420 | |
421 | if (base > inode->i_size) |
422 | inode->i_size = base; |
423 | |
424 | err = 0; |
425 | |
426 | out: |
427 | kunmap_local(buffer); |
428 | unlock_page(page); |
429 | |
430 | return err; |
431 | } |
432 | |
433 | static int hostfs_read_folio(struct file *file, struct folio *folio) |
434 | { |
435 | struct page *page = &folio->page; |
436 | char *buffer; |
437 | loff_t start = page_offset(page); |
438 | int bytes_read, ret = 0; |
439 | |
440 | buffer = kmap_local_page(page); |
441 | bytes_read = read_file(FILE_HOSTFS_I(file)->fd, offset: &start, buf: buffer, |
442 | PAGE_SIZE); |
443 | if (bytes_read < 0) { |
444 | ClearPageUptodate(page); |
445 | SetPageError(page); |
446 | ret = bytes_read; |
447 | goto out; |
448 | } |
449 | |
450 | memset(buffer + bytes_read, 0, PAGE_SIZE - bytes_read); |
451 | |
452 | ClearPageError(page); |
453 | SetPageUptodate(page); |
454 | |
455 | out: |
456 | flush_dcache_page(page); |
457 | kunmap_local(buffer); |
458 | unlock_page(page); |
459 | |
460 | return ret; |
461 | } |
462 | |
463 | static int hostfs_write_begin(struct file *file, struct address_space *mapping, |
464 | loff_t pos, unsigned len, |
465 | struct page **pagep, void **fsdata) |
466 | { |
467 | pgoff_t index = pos >> PAGE_SHIFT; |
468 | |
469 | *pagep = grab_cache_page_write_begin(mapping, index); |
470 | if (!*pagep) |
471 | return -ENOMEM; |
472 | return 0; |
473 | } |
474 | |
475 | static int hostfs_write_end(struct file *file, struct address_space *mapping, |
476 | loff_t pos, unsigned len, unsigned copied, |
477 | struct page *page, void *fsdata) |
478 | { |
479 | struct inode *inode = mapping->host; |
480 | void *buffer; |
481 | unsigned from = pos & (PAGE_SIZE - 1); |
482 | int err; |
483 | |
484 | buffer = kmap_local_page(page); |
485 | err = write_file(FILE_HOSTFS_I(file)->fd, offset: &pos, buf: buffer + from, len: copied); |
486 | kunmap_local(buffer); |
487 | |
488 | if (!PageUptodate(page) && err == PAGE_SIZE) |
489 | SetPageUptodate(page); |
490 | |
491 | /* |
492 | * If err > 0, write_file has added err to pos, so we are comparing |
493 | * i_size against the last byte written. |
494 | */ |
495 | if (err > 0 && (pos > inode->i_size)) |
496 | inode->i_size = pos; |
497 | unlock_page(page); |
498 | put_page(page); |
499 | |
500 | return err; |
501 | } |
502 | |
503 | static const struct address_space_operations hostfs_aops = { |
504 | .writepage = hostfs_writepage, |
505 | .read_folio = hostfs_read_folio, |
506 | .dirty_folio = filemap_dirty_folio, |
507 | .write_begin = hostfs_write_begin, |
508 | .write_end = hostfs_write_end, |
509 | }; |
510 | |
511 | static int hostfs_inode_update(struct inode *ino, const struct hostfs_stat *st) |
512 | { |
513 | set_nlink(inode: ino, nlink: st->nlink); |
514 | i_uid_write(inode: ino, uid: st->uid); |
515 | i_gid_write(inode: ino, gid: st->gid); |
516 | inode_set_atime_to_ts(inode: ino, ts: (struct timespec64){ |
517 | st->atime.tv_sec, |
518 | st->atime.tv_nsec, |
519 | }); |
520 | inode_set_mtime_to_ts(inode: ino, ts: (struct timespec64){ |
521 | st->mtime.tv_sec, |
522 | st->mtime.tv_nsec, |
523 | }); |
524 | inode_set_ctime(inode: ino, sec: st->ctime.tv_sec, nsec: st->ctime.tv_nsec); |
525 | ino->i_size = st->size; |
526 | ino->i_blocks = st->blocks; |
527 | return 0; |
528 | } |
529 | |
530 | static int hostfs_inode_set(struct inode *ino, void *data) |
531 | { |
532 | struct hostfs_stat *st = data; |
533 | dev_t rdev; |
534 | |
535 | /* Reencode maj and min with the kernel encoding.*/ |
536 | rdev = MKDEV(st->maj, st->min); |
537 | |
538 | switch (st->mode & S_IFMT) { |
539 | case S_IFLNK: |
540 | ino->i_op = &hostfs_link_iops; |
541 | break; |
542 | case S_IFDIR: |
543 | ino->i_op = &hostfs_dir_iops; |
544 | ino->i_fop = &hostfs_dir_fops; |
545 | break; |
546 | case S_IFCHR: |
547 | case S_IFBLK: |
548 | case S_IFIFO: |
549 | case S_IFSOCK: |
550 | init_special_inode(ino, st->mode & S_IFMT, rdev); |
551 | ino->i_op = &hostfs_iops; |
552 | break; |
553 | case S_IFREG: |
554 | ino->i_op = &hostfs_iops; |
555 | ino->i_fop = &hostfs_file_fops; |
556 | ino->i_mapping->a_ops = &hostfs_aops; |
557 | break; |
558 | default: |
559 | return -EIO; |
560 | } |
561 | |
562 | HOSTFS_I(inode: ino)->dev = st->dev; |
563 | ino->i_ino = st->ino; |
564 | ino->i_mode = st->mode; |
565 | return hostfs_inode_update(ino, st); |
566 | } |
567 | |
568 | static int hostfs_inode_test(struct inode *inode, void *data) |
569 | { |
570 | const struct hostfs_stat *st = data; |
571 | |
572 | return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == st->dev; |
573 | } |
574 | |
575 | static struct inode *hostfs_iget(struct super_block *sb, char *name) |
576 | { |
577 | struct inode *inode; |
578 | struct hostfs_stat st; |
579 | int err = stat_file(path: name, p: &st, fd: -1); |
580 | |
581 | if (err) |
582 | return ERR_PTR(error: err); |
583 | |
584 | inode = iget5_locked(sb, st.ino, test: hostfs_inode_test, set: hostfs_inode_set, |
585 | &st); |
586 | if (!inode) |
587 | return ERR_PTR(error: -ENOMEM); |
588 | |
589 | if (inode->i_state & I_NEW) { |
590 | unlock_new_inode(inode); |
591 | } else { |
592 | spin_lock(lock: &inode->i_lock); |
593 | hostfs_inode_update(ino: inode, st: &st); |
594 | spin_unlock(lock: &inode->i_lock); |
595 | } |
596 | |
597 | return inode; |
598 | } |
599 | |
600 | static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir, |
601 | struct dentry *dentry, umode_t mode, bool excl) |
602 | { |
603 | struct inode *inode; |
604 | char *name; |
605 | int fd; |
606 | |
607 | name = dentry_name(dentry); |
608 | if (name == NULL) |
609 | return -ENOMEM; |
610 | |
611 | fd = file_create(name, mode: mode & 0777); |
612 | if (fd < 0) { |
613 | __putname(name); |
614 | return fd; |
615 | } |
616 | |
617 | inode = hostfs_iget(sb: dir->i_sb, name); |
618 | __putname(name); |
619 | if (IS_ERR(ptr: inode)) |
620 | return PTR_ERR(ptr: inode); |
621 | |
622 | HOSTFS_I(inode)->fd = fd; |
623 | HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE; |
624 | d_instantiate(dentry, inode); |
625 | return 0; |
626 | } |
627 | |
628 | static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry, |
629 | unsigned int flags) |
630 | { |
631 | struct inode *inode = NULL; |
632 | char *name; |
633 | |
634 | name = dentry_name(dentry); |
635 | if (name == NULL) |
636 | return ERR_PTR(error: -ENOMEM); |
637 | |
638 | inode = hostfs_iget(sb: ino->i_sb, name); |
639 | __putname(name); |
640 | if (inode == ERR_PTR(error: -ENOENT)) |
641 | inode = NULL; |
642 | |
643 | return d_splice_alias(inode, dentry); |
644 | } |
645 | |
646 | static int hostfs_link(struct dentry *to, struct inode *ino, |
647 | struct dentry *from) |
648 | { |
649 | char *from_name, *to_name; |
650 | int err; |
651 | |
652 | if ((from_name = dentry_name(dentry: from)) == NULL) |
653 | return -ENOMEM; |
654 | to_name = dentry_name(dentry: to); |
655 | if (to_name == NULL) { |
656 | __putname(from_name); |
657 | return -ENOMEM; |
658 | } |
659 | err = link_file(to: to_name, from: from_name); |
660 | __putname(from_name); |
661 | __putname(to_name); |
662 | return err; |
663 | } |
664 | |
665 | static int hostfs_unlink(struct inode *ino, struct dentry *dentry) |
666 | { |
667 | char *file; |
668 | int err; |
669 | |
670 | if (append) |
671 | return -EPERM; |
672 | |
673 | if ((file = dentry_name(dentry)) == NULL) |
674 | return -ENOMEM; |
675 | |
676 | err = unlink_file(file); |
677 | __putname(file); |
678 | return err; |
679 | } |
680 | |
681 | static int hostfs_symlink(struct mnt_idmap *idmap, struct inode *ino, |
682 | struct dentry *dentry, const char *to) |
683 | { |
684 | char *file; |
685 | int err; |
686 | |
687 | if ((file = dentry_name(dentry)) == NULL) |
688 | return -ENOMEM; |
689 | err = make_symlink(from: file, to); |
690 | __putname(file); |
691 | return err; |
692 | } |
693 | |
694 | static int hostfs_mkdir(struct mnt_idmap *idmap, struct inode *ino, |
695 | struct dentry *dentry, umode_t mode) |
696 | { |
697 | char *file; |
698 | int err; |
699 | |
700 | if ((file = dentry_name(dentry)) == NULL) |
701 | return -ENOMEM; |
702 | err = do_mkdir(file, mode); |
703 | __putname(file); |
704 | return err; |
705 | } |
706 | |
707 | static int hostfs_rmdir(struct inode *ino, struct dentry *dentry) |
708 | { |
709 | char *file; |
710 | int err; |
711 | |
712 | if ((file = dentry_name(dentry)) == NULL) |
713 | return -ENOMEM; |
714 | err = hostfs_do_rmdir(file); |
715 | __putname(file); |
716 | return err; |
717 | } |
718 | |
719 | static int hostfs_mknod(struct mnt_idmap *idmap, struct inode *dir, |
720 | struct dentry *dentry, umode_t mode, dev_t dev) |
721 | { |
722 | struct inode *inode; |
723 | char *name; |
724 | int err; |
725 | |
726 | name = dentry_name(dentry); |
727 | if (name == NULL) |
728 | return -ENOMEM; |
729 | |
730 | err = do_mknod(file: name, mode, MAJOR(dev), MINOR(dev)); |
731 | if (err) { |
732 | __putname(name); |
733 | return err; |
734 | } |
735 | |
736 | inode = hostfs_iget(sb: dir->i_sb, name); |
737 | __putname(name); |
738 | if (IS_ERR(ptr: inode)) |
739 | return PTR_ERR(ptr: inode); |
740 | |
741 | d_instantiate(dentry, inode); |
742 | return 0; |
743 | } |
744 | |
745 | static int hostfs_rename2(struct mnt_idmap *idmap, |
746 | struct inode *old_dir, struct dentry *old_dentry, |
747 | struct inode *new_dir, struct dentry *new_dentry, |
748 | unsigned int flags) |
749 | { |
750 | char *old_name, *new_name; |
751 | int err; |
752 | |
753 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) |
754 | return -EINVAL; |
755 | |
756 | old_name = dentry_name(dentry: old_dentry); |
757 | if (old_name == NULL) |
758 | return -ENOMEM; |
759 | new_name = dentry_name(dentry: new_dentry); |
760 | if (new_name == NULL) { |
761 | __putname(old_name); |
762 | return -ENOMEM; |
763 | } |
764 | if (!flags) |
765 | err = rename_file(from: old_name, to: new_name); |
766 | else |
767 | err = rename2_file(from: old_name, to: new_name, flags); |
768 | |
769 | __putname(old_name); |
770 | __putname(new_name); |
771 | return err; |
772 | } |
773 | |
774 | static int hostfs_permission(struct mnt_idmap *idmap, |
775 | struct inode *ino, int desired) |
776 | { |
777 | char *name; |
778 | int r = 0, w = 0, x = 0, err; |
779 | |
780 | if (desired & MAY_NOT_BLOCK) |
781 | return -ECHILD; |
782 | |
783 | if (desired & MAY_READ) r = 1; |
784 | if (desired & MAY_WRITE) w = 1; |
785 | if (desired & MAY_EXEC) x = 1; |
786 | name = inode_name(ino); |
787 | if (name == NULL) |
788 | return -ENOMEM; |
789 | |
790 | if (S_ISCHR(ino->i_mode) || S_ISBLK(ino->i_mode) || |
791 | S_ISFIFO(ino->i_mode) || S_ISSOCK(ino->i_mode)) |
792 | err = 0; |
793 | else |
794 | err = access_file(path: name, r, w, x); |
795 | __putname(name); |
796 | if (!err) |
797 | err = generic_permission(&nop_mnt_idmap, ino, desired); |
798 | return err; |
799 | } |
800 | |
801 | static int hostfs_setattr(struct mnt_idmap *idmap, |
802 | struct dentry *dentry, struct iattr *attr) |
803 | { |
804 | struct inode *inode = d_inode(dentry); |
805 | struct hostfs_iattr attrs; |
806 | char *name; |
807 | int err; |
808 | |
809 | int fd = HOSTFS_I(inode)->fd; |
810 | |
811 | err = setattr_prepare(&nop_mnt_idmap, dentry, attr); |
812 | if (err) |
813 | return err; |
814 | |
815 | if (append) |
816 | attr->ia_valid &= ~ATTR_SIZE; |
817 | |
818 | attrs.ia_valid = 0; |
819 | if (attr->ia_valid & ATTR_MODE) { |
820 | attrs.ia_valid |= HOSTFS_ATTR_MODE; |
821 | attrs.ia_mode = attr->ia_mode; |
822 | } |
823 | if (attr->ia_valid & ATTR_UID) { |
824 | attrs.ia_valid |= HOSTFS_ATTR_UID; |
825 | attrs.ia_uid = from_kuid(to: &init_user_ns, uid: attr->ia_uid); |
826 | } |
827 | if (attr->ia_valid & ATTR_GID) { |
828 | attrs.ia_valid |= HOSTFS_ATTR_GID; |
829 | attrs.ia_gid = from_kgid(to: &init_user_ns, gid: attr->ia_gid); |
830 | } |
831 | if (attr->ia_valid & ATTR_SIZE) { |
832 | attrs.ia_valid |= HOSTFS_ATTR_SIZE; |
833 | attrs.ia_size = attr->ia_size; |
834 | } |
835 | if (attr->ia_valid & ATTR_ATIME) { |
836 | attrs.ia_valid |= HOSTFS_ATTR_ATIME; |
837 | attrs.ia_atime = (struct hostfs_timespec) |
838 | { attr->ia_atime.tv_sec, attr->ia_atime.tv_nsec }; |
839 | } |
840 | if (attr->ia_valid & ATTR_MTIME) { |
841 | attrs.ia_valid |= HOSTFS_ATTR_MTIME; |
842 | attrs.ia_mtime = (struct hostfs_timespec) |
843 | { attr->ia_mtime.tv_sec, attr->ia_mtime.tv_nsec }; |
844 | } |
845 | if (attr->ia_valid & ATTR_CTIME) { |
846 | attrs.ia_valid |= HOSTFS_ATTR_CTIME; |
847 | attrs.ia_ctime = (struct hostfs_timespec) |
848 | { attr->ia_ctime.tv_sec, attr->ia_ctime.tv_nsec }; |
849 | } |
850 | if (attr->ia_valid & ATTR_ATIME_SET) { |
851 | attrs.ia_valid |= HOSTFS_ATTR_ATIME_SET; |
852 | } |
853 | if (attr->ia_valid & ATTR_MTIME_SET) { |
854 | attrs.ia_valid |= HOSTFS_ATTR_MTIME_SET; |
855 | } |
856 | name = dentry_name(dentry); |
857 | if (name == NULL) |
858 | return -ENOMEM; |
859 | err = set_attr(file: name, attrs: &attrs, fd); |
860 | __putname(name); |
861 | if (err) |
862 | return err; |
863 | |
864 | if ((attr->ia_valid & ATTR_SIZE) && |
865 | attr->ia_size != i_size_read(inode)) |
866 | truncate_setsize(inode, newsize: attr->ia_size); |
867 | |
868 | setattr_copy(&nop_mnt_idmap, inode, attr); |
869 | mark_inode_dirty(inode); |
870 | return 0; |
871 | } |
872 | |
873 | static const struct inode_operations hostfs_iops = { |
874 | .permission = hostfs_permission, |
875 | .setattr = hostfs_setattr, |
876 | }; |
877 | |
878 | static const struct inode_operations hostfs_dir_iops = { |
879 | .create = hostfs_create, |
880 | .lookup = hostfs_lookup, |
881 | .link = hostfs_link, |
882 | .unlink = hostfs_unlink, |
883 | .symlink = hostfs_symlink, |
884 | .mkdir = hostfs_mkdir, |
885 | .rmdir = hostfs_rmdir, |
886 | .mknod = hostfs_mknod, |
887 | .rename = hostfs_rename2, |
888 | .permission = hostfs_permission, |
889 | .setattr = hostfs_setattr, |
890 | }; |
891 | |
892 | static const char *hostfs_get_link(struct dentry *dentry, |
893 | struct inode *inode, |
894 | struct delayed_call *done) |
895 | { |
896 | char *link; |
897 | if (!dentry) |
898 | return ERR_PTR(error: -ECHILD); |
899 | link = kmalloc(PATH_MAX, GFP_KERNEL); |
900 | if (link) { |
901 | char *path = dentry_name(dentry); |
902 | int err = -ENOMEM; |
903 | if (path) { |
904 | err = hostfs_do_readlink(file: path, buf: link, PATH_MAX); |
905 | if (err == PATH_MAX) |
906 | err = -E2BIG; |
907 | __putname(path); |
908 | } |
909 | if (err < 0) { |
910 | kfree(objp: link); |
911 | return ERR_PTR(error: err); |
912 | } |
913 | } else { |
914 | return ERR_PTR(error: -ENOMEM); |
915 | } |
916 | |
917 | set_delayed_call(call: done, fn: kfree_link, arg: link); |
918 | return link; |
919 | } |
920 | |
921 | static const struct inode_operations hostfs_link_iops = { |
922 | .get_link = hostfs_get_link, |
923 | }; |
924 | |
925 | static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) |
926 | { |
927 | struct inode *root_inode; |
928 | char *host_root_path, *req_root = d; |
929 | int err; |
930 | |
931 | sb->s_blocksize = 1024; |
932 | sb->s_blocksize_bits = 10; |
933 | sb->s_magic = HOSTFS_SUPER_MAGIC; |
934 | sb->s_op = &hostfs_sbops; |
935 | sb->s_d_op = &simple_dentry_operations; |
936 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
937 | err = super_setup_bdi(sb); |
938 | if (err) |
939 | return err; |
940 | |
941 | /* NULL is printed as '(null)' by printf(): avoid that. */ |
942 | if (req_root == NULL) |
943 | req_root = "" ; |
944 | |
945 | sb->s_fs_info = host_root_path = |
946 | kasprintf(GFP_KERNEL, fmt: "%s/%s" , root_ino, req_root); |
947 | if (host_root_path == NULL) |
948 | return -ENOMEM; |
949 | |
950 | root_inode = hostfs_iget(sb, name: host_root_path); |
951 | if (IS_ERR(ptr: root_inode)) |
952 | return PTR_ERR(ptr: root_inode); |
953 | |
954 | if (S_ISLNK(root_inode->i_mode)) { |
955 | char *name; |
956 | |
957 | iput(root_inode); |
958 | name = follow_link(link: host_root_path); |
959 | if (IS_ERR(ptr: name)) |
960 | return PTR_ERR(ptr: name); |
961 | |
962 | root_inode = hostfs_iget(sb, name); |
963 | kfree(objp: name); |
964 | if (IS_ERR(ptr: root_inode)) |
965 | return PTR_ERR(ptr: root_inode); |
966 | } |
967 | |
968 | sb->s_root = d_make_root(root_inode); |
969 | if (sb->s_root == NULL) |
970 | return -ENOMEM; |
971 | |
972 | return 0; |
973 | } |
974 | |
975 | static struct dentry *hostfs_read_sb(struct file_system_type *type, |
976 | int flags, const char *dev_name, |
977 | void *data) |
978 | { |
979 | return mount_nodev(fs_type: type, flags, data, fill_super: hostfs_fill_sb_common); |
980 | } |
981 | |
982 | static void hostfs_kill_sb(struct super_block *s) |
983 | { |
984 | kill_anon_super(sb: s); |
985 | kfree(objp: s->s_fs_info); |
986 | } |
987 | |
988 | static struct file_system_type hostfs_type = { |
989 | .owner = THIS_MODULE, |
990 | .name = "hostfs" , |
991 | .mount = hostfs_read_sb, |
992 | .kill_sb = hostfs_kill_sb, |
993 | .fs_flags = 0, |
994 | }; |
995 | MODULE_ALIAS_FS("hostfs" ); |
996 | |
997 | static int __init init_hostfs(void) |
998 | { |
999 | hostfs_inode_cache = KMEM_CACHE(hostfs_inode_info, 0); |
1000 | if (!hostfs_inode_cache) |
1001 | return -ENOMEM; |
1002 | return register_filesystem(&hostfs_type); |
1003 | } |
1004 | |
1005 | static void __exit exit_hostfs(void) |
1006 | { |
1007 | unregister_filesystem(&hostfs_type); |
1008 | kmem_cache_destroy(s: hostfs_inode_cache); |
1009 | } |
1010 | |
1011 | module_init(init_hostfs) |
1012 | module_exit(exit_hostfs) |
1013 | MODULE_LICENSE("GPL" ); |
1014 | |