diff options
Diffstat (limited to 'fs/hostfs')
-rw-r--r-- | fs/hostfs/Makefile | 11 | ||||
-rw-r--r-- | fs/hostfs/hostfs.h | 100 | ||||
-rw-r--r-- | fs/hostfs/hostfs_kern.c | 1045 | ||||
-rw-r--r-- | fs/hostfs/hostfs_user.c | 362 |
4 files changed, 1518 insertions, 0 deletions
diff --git a/fs/hostfs/Makefile b/fs/hostfs/Makefile new file mode 100644 index 000000000000..d5beaffad43b --- /dev/null +++ b/fs/hostfs/Makefile | |||
@@ -0,0 +1,11 @@ | |||
1 | # | ||
2 | # Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | # Licensed under the GPL | ||
4 | # | ||
5 | |||
6 | hostfs-objs := hostfs_kern.o hostfs_user.o | ||
7 | |||
8 | obj-y := | ||
9 | obj-$(CONFIG_HOSTFS) += hostfs.o | ||
10 | |||
11 | include arch/um/scripts/Makefile.rules | ||
diff --git a/fs/hostfs/hostfs.h b/fs/hostfs/hostfs.h new file mode 100644 index 000000000000..c1516d013bf6 --- /dev/null +++ b/fs/hostfs/hostfs.h | |||
@@ -0,0 +1,100 @@ | |||
1 | #ifndef __UM_FS_HOSTFS | ||
2 | #define __UM_FS_HOSTFS | ||
3 | |||
4 | #include "os.h" | ||
5 | |||
6 | /* These are exactly the same definitions as in fs.h, but the names are | ||
7 | * changed so that this file can be included in both kernel and user files. | ||
8 | */ | ||
9 | |||
10 | #define HOSTFS_ATTR_MODE 1 | ||
11 | #define HOSTFS_ATTR_UID 2 | ||
12 | #define HOSTFS_ATTR_GID 4 | ||
13 | #define HOSTFS_ATTR_SIZE 8 | ||
14 | #define HOSTFS_ATTR_ATIME 16 | ||
15 | #define HOSTFS_ATTR_MTIME 32 | ||
16 | #define HOSTFS_ATTR_CTIME 64 | ||
17 | #define HOSTFS_ATTR_ATIME_SET 128 | ||
18 | #define HOSTFS_ATTR_MTIME_SET 256 | ||
19 | |||
20 | /* These two are unused by hostfs. */ | ||
21 | #define HOSTFS_ATTR_FORCE 512 /* Not a change, but a change it */ | ||
22 | #define HOSTFS_ATTR_ATTR_FLAG 1024 | ||
23 | |||
24 | /* If you are very careful, you'll notice that these two are missing: | ||
25 | * | ||
26 | * #define ATTR_KILL_SUID 2048 | ||
27 | * #define ATTR_KILL_SGID 4096 | ||
28 | * | ||
29 | * and this is because they were added in 2.5 development in this patch: | ||
30 | * | ||
31 | * http://linux.bkbits.net:8080/linux-2.5/ | ||
32 | * cset@3caf4a12k4XgDzK7wyK-TGpSZ9u2Ww?nav=index.html | ||
33 | * |src/.|src/include|src/include/linux|related/include/linux/fs.h | ||
34 | * | ||
35 | * Actually, they are not needed by most ->setattr() methods - they are set by | ||
36 | * callers of notify_change() to notify that the setuid/setgid bits must be | ||
37 | * dropped. | ||
38 | * notify_change() will delete those flags, make sure attr->ia_valid & ATTR_MODE | ||
39 | * is on, and remove the appropriate bits from attr->ia_mode (attr is a | ||
40 | * "struct iattr *"). -BlaisorBlade | ||
41 | */ | ||
42 | |||
43 | struct hostfs_iattr { | ||
44 | unsigned int ia_valid; | ||
45 | mode_t ia_mode; | ||
46 | uid_t ia_uid; | ||
47 | gid_t ia_gid; | ||
48 | loff_t ia_size; | ||
49 | struct timespec ia_atime; | ||
50 | struct timespec ia_mtime; | ||
51 | struct timespec ia_ctime; | ||
52 | unsigned int ia_attr_flags; | ||
53 | }; | ||
54 | |||
55 | extern int stat_file(const char *path, unsigned long long *inode_out, | ||
56 | int *mode_out, int *nlink_out, int *uid_out, int *gid_out, | ||
57 | unsigned long long *size_out, struct timespec *atime_out, | ||
58 | struct timespec *mtime_out, struct timespec *ctime_out, | ||
59 | int *blksize_out, unsigned long long *blocks_out); | ||
60 | extern int access_file(char *path, int r, int w, int x); | ||
61 | extern int open_file(char *path, int r, int w, int append); | ||
62 | extern int file_type(const char *path, int *maj, int *min); | ||
63 | extern void *open_dir(char *path, int *err_out); | ||
64 | extern char *read_dir(void *stream, unsigned long long *pos, | ||
65 | unsigned long long *ino_out, int *len_out); | ||
66 | extern void close_file(void *stream); | ||
67 | extern void close_dir(void *stream); | ||
68 | extern int read_file(int fd, unsigned long long *offset, char *buf, int len); | ||
69 | extern int write_file(int fd, unsigned long long *offset, const char *buf, | ||
70 | int len); | ||
71 | extern int lseek_file(int fd, long long offset, int whence); | ||
72 | extern int file_create(char *name, int ur, int uw, int ux, int gr, | ||
73 | int gw, int gx, int or, int ow, int ox); | ||
74 | extern int set_attr(const char *file, struct hostfs_iattr *attrs); | ||
75 | extern int make_symlink(const char *from, const char *to); | ||
76 | extern int unlink_file(const char *file); | ||
77 | extern int do_mkdir(const char *file, int mode); | ||
78 | extern int do_rmdir(const char *file); | ||
79 | extern int do_mknod(const char *file, int mode, int dev); | ||
80 | extern int link_file(const char *from, const char *to); | ||
81 | extern int do_readlink(char *file, char *buf, int size); | ||
82 | extern int rename_file(char *from, char *to); | ||
83 | extern int do_statfs(char *root, long *bsize_out, long long *blocks_out, | ||
84 | long long *bfree_out, long long *bavail_out, | ||
85 | long long *files_out, long long *ffree_out, | ||
86 | void *fsid_out, int fsid_size, long *namelen_out, | ||
87 | long *spare_out); | ||
88 | |||
89 | #endif | ||
90 | |||
91 | /* | ||
92 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
93 | * Emacs will notice this stuff at the end of the file and automatically | ||
94 | * adjust the settings for this buffer only. This must remain at the end | ||
95 | * of the file. | ||
96 | * --------------------------------------------------------------------------- | ||
97 | * Local variables: | ||
98 | * c-file-style: "linux" | ||
99 | * End: | ||
100 | */ | ||
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 | */ | ||
diff --git a/fs/hostfs/hostfs_user.c b/fs/hostfs/hostfs_user.c new file mode 100644 index 000000000000..4796e8490f7d --- /dev/null +++ b/fs/hostfs/hostfs_user.c | |||
@@ -0,0 +1,362 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2000 Jeff Dike (jdike@karaya.com) | ||
3 | * Licensed under the GPL | ||
4 | */ | ||
5 | |||
6 | #include <unistd.h> | ||
7 | #include <stdio.h> | ||
8 | #include <fcntl.h> | ||
9 | #include <dirent.h> | ||
10 | #include <errno.h> | ||
11 | #include <utime.h> | ||
12 | #include <string.h> | ||
13 | #include <sys/stat.h> | ||
14 | #include <sys/time.h> | ||
15 | #include <sys/vfs.h> | ||
16 | #include "hostfs.h" | ||
17 | #include "kern_util.h" | ||
18 | #include "user.h" | ||
19 | |||
20 | int stat_file(const char *path, unsigned long long *inode_out, int *mode_out, | ||
21 | int *nlink_out, int *uid_out, int *gid_out, | ||
22 | unsigned long long *size_out, struct timespec *atime_out, | ||
23 | struct timespec *mtime_out, struct timespec *ctime_out, | ||
24 | int *blksize_out, unsigned long long *blocks_out) | ||
25 | { | ||
26 | struct stat64 buf; | ||
27 | |||
28 | if(lstat64(path, &buf) < 0) | ||
29 | return(-errno); | ||
30 | |||
31 | if(inode_out != NULL) *inode_out = buf.st_ino; | ||
32 | if(mode_out != NULL) *mode_out = buf.st_mode; | ||
33 | if(nlink_out != NULL) *nlink_out = buf.st_nlink; | ||
34 | if(uid_out != NULL) *uid_out = buf.st_uid; | ||
35 | if(gid_out != NULL) *gid_out = buf.st_gid; | ||
36 | if(size_out != NULL) *size_out = buf.st_size; | ||
37 | if(atime_out != NULL) { | ||
38 | atime_out->tv_sec = buf.st_atime; | ||
39 | atime_out->tv_nsec = 0; | ||
40 | } | ||
41 | if(mtime_out != NULL) { | ||
42 | mtime_out->tv_sec = buf.st_mtime; | ||
43 | mtime_out->tv_nsec = 0; | ||
44 | } | ||
45 | if(ctime_out != NULL) { | ||
46 | ctime_out->tv_sec = buf.st_ctime; | ||
47 | ctime_out->tv_nsec = 0; | ||
48 | } | ||
49 | if(blksize_out != NULL) *blksize_out = buf.st_blksize; | ||
50 | if(blocks_out != NULL) *blocks_out = buf.st_blocks; | ||
51 | return(0); | ||
52 | } | ||
53 | |||
54 | int file_type(const char *path, int *maj, int *min) | ||
55 | { | ||
56 | struct stat64 buf; | ||
57 | |||
58 | if(lstat64(path, &buf) < 0) | ||
59 | return(-errno); | ||
60 | /*We cannot pass rdev as is because glibc and the kernel disagree | ||
61 | *about its definition.*/ | ||
62 | if(maj != NULL) | ||
63 | *maj = major(buf.st_rdev); | ||
64 | if(min != NULL) | ||
65 | *min = minor(buf.st_rdev); | ||
66 | |||
67 | if(S_ISDIR(buf.st_mode)) return(OS_TYPE_DIR); | ||
68 | else if(S_ISLNK(buf.st_mode)) return(OS_TYPE_SYMLINK); | ||
69 | else if(S_ISCHR(buf.st_mode)) return(OS_TYPE_CHARDEV); | ||
70 | else if(S_ISBLK(buf.st_mode)) return(OS_TYPE_BLOCKDEV); | ||
71 | else if(S_ISFIFO(buf.st_mode))return(OS_TYPE_FIFO); | ||
72 | else if(S_ISSOCK(buf.st_mode))return(OS_TYPE_SOCK); | ||
73 | else return(OS_TYPE_FILE); | ||
74 | } | ||
75 | |||
76 | int access_file(char *path, int r, int w, int x) | ||
77 | { | ||
78 | int mode = 0; | ||
79 | |||
80 | if(r) mode = R_OK; | ||
81 | if(w) mode |= W_OK; | ||
82 | if(x) mode |= X_OK; | ||
83 | if(access(path, mode) != 0) return(-errno); | ||
84 | else return(0); | ||
85 | } | ||
86 | |||
87 | int open_file(char *path, int r, int w, int append) | ||
88 | { | ||
89 | int mode = 0, fd; | ||
90 | |||
91 | if(r && !w) | ||
92 | mode = O_RDONLY; | ||
93 | else if(!r && w) | ||
94 | mode = O_WRONLY; | ||
95 | else if(r && w) | ||
96 | mode = O_RDWR; | ||
97 | else panic("Impossible mode in open_file"); | ||
98 | |||
99 | if(append) | ||
100 | mode |= O_APPEND; | ||
101 | fd = open64(path, mode); | ||
102 | if(fd < 0) return(-errno); | ||
103 | else return(fd); | ||
104 | } | ||
105 | |||
106 | void *open_dir(char *path, int *err_out) | ||
107 | { | ||
108 | DIR *dir; | ||
109 | |||
110 | dir = opendir(path); | ||
111 | *err_out = errno; | ||
112 | if(dir == NULL) return(NULL); | ||
113 | return(dir); | ||
114 | } | ||
115 | |||
116 | char *read_dir(void *stream, unsigned long long *pos, | ||
117 | unsigned long long *ino_out, int *len_out) | ||
118 | { | ||
119 | DIR *dir = stream; | ||
120 | struct dirent *ent; | ||
121 | |||
122 | seekdir(dir, *pos); | ||
123 | ent = readdir(dir); | ||
124 | if(ent == NULL) return(NULL); | ||
125 | *len_out = strlen(ent->d_name); | ||
126 | *ino_out = ent->d_ino; | ||
127 | *pos = telldir(dir); | ||
128 | return(ent->d_name); | ||
129 | } | ||
130 | |||
131 | int read_file(int fd, unsigned long long *offset, char *buf, int len) | ||
132 | { | ||
133 | int n; | ||
134 | |||
135 | n = pread64(fd, buf, len, *offset); | ||
136 | if(n < 0) return(-errno); | ||
137 | *offset += n; | ||
138 | return(n); | ||
139 | } | ||
140 | |||
141 | int write_file(int fd, unsigned long long *offset, const char *buf, int len) | ||
142 | { | ||
143 | int n; | ||
144 | |||
145 | n = pwrite64(fd, buf, len, *offset); | ||
146 | if(n < 0) return(-errno); | ||
147 | *offset += n; | ||
148 | return(n); | ||
149 | } | ||
150 | |||
151 | int lseek_file(int fd, long long offset, int whence) | ||
152 | { | ||
153 | int ret; | ||
154 | |||
155 | ret = lseek64(fd, offset, whence); | ||
156 | if(ret < 0) return(-errno); | ||
157 | return(0); | ||
158 | } | ||
159 | |||
160 | void close_file(void *stream) | ||
161 | { | ||
162 | close(*((int *) stream)); | ||
163 | } | ||
164 | |||
165 | void close_dir(void *stream) | ||
166 | { | ||
167 | closedir(stream); | ||
168 | } | ||
169 | |||
170 | int file_create(char *name, int ur, int uw, int ux, int gr, | ||
171 | int gw, int gx, int or, int ow, int ox) | ||
172 | { | ||
173 | int mode, fd; | ||
174 | |||
175 | mode = 0; | ||
176 | mode |= ur ? S_IRUSR : 0; | ||
177 | mode |= uw ? S_IWUSR : 0; | ||
178 | mode |= ux ? S_IXUSR : 0; | ||
179 | mode |= gr ? S_IRGRP : 0; | ||
180 | mode |= gw ? S_IWGRP : 0; | ||
181 | mode |= gx ? S_IXGRP : 0; | ||
182 | mode |= or ? S_IROTH : 0; | ||
183 | mode |= ow ? S_IWOTH : 0; | ||
184 | mode |= ox ? S_IXOTH : 0; | ||
185 | fd = open64(name, O_CREAT | O_RDWR, mode); | ||
186 | if(fd < 0) | ||
187 | return(-errno); | ||
188 | return(fd); | ||
189 | } | ||
190 | |||
191 | int set_attr(const char *file, struct hostfs_iattr *attrs) | ||
192 | { | ||
193 | struct utimbuf buf; | ||
194 | int err, ma; | ||
195 | |||
196 | if(attrs->ia_valid & HOSTFS_ATTR_MODE){ | ||
197 | if(chmod(file, attrs->ia_mode) != 0) return(-errno); | ||
198 | } | ||
199 | if(attrs->ia_valid & HOSTFS_ATTR_UID){ | ||
200 | if(chown(file, attrs->ia_uid, -1)) return(-errno); | ||
201 | } | ||
202 | if(attrs->ia_valid & HOSTFS_ATTR_GID){ | ||
203 | if(chown(file, -1, attrs->ia_gid)) return(-errno); | ||
204 | } | ||
205 | if(attrs->ia_valid & HOSTFS_ATTR_SIZE){ | ||
206 | if(truncate(file, attrs->ia_size)) return(-errno); | ||
207 | } | ||
208 | ma = HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET; | ||
209 | if((attrs->ia_valid & ma) == ma){ | ||
210 | buf.actime = attrs->ia_atime.tv_sec; | ||
211 | buf.modtime = attrs->ia_mtime.tv_sec; | ||
212 | if(utime(file, &buf) != 0) return(-errno); | ||
213 | } | ||
214 | else { | ||
215 | struct timespec ts; | ||
216 | |||
217 | if(attrs->ia_valid & HOSTFS_ATTR_ATIME_SET){ | ||
218 | err = stat_file(file, NULL, NULL, NULL, NULL, NULL, | ||
219 | NULL, NULL, &ts, NULL, NULL, NULL); | ||
220 | if(err != 0) | ||
221 | return(err); | ||
222 | buf.actime = attrs->ia_atime.tv_sec; | ||
223 | buf.modtime = ts.tv_sec; | ||
224 | if(utime(file, &buf) != 0) | ||
225 | return(-errno); | ||
226 | } | ||
227 | if(attrs->ia_valid & HOSTFS_ATTR_MTIME_SET){ | ||
228 | err = stat_file(file, NULL, NULL, NULL, NULL, NULL, | ||
229 | NULL, &ts, NULL, NULL, NULL, NULL); | ||
230 | if(err != 0) | ||
231 | return(err); | ||
232 | buf.actime = ts.tv_sec; | ||
233 | buf.modtime = attrs->ia_mtime.tv_sec; | ||
234 | if(utime(file, &buf) != 0) | ||
235 | return(-errno); | ||
236 | } | ||
237 | } | ||
238 | if(attrs->ia_valid & HOSTFS_ATTR_CTIME) ; | ||
239 | if(attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)){ | ||
240 | err = stat_file(file, NULL, NULL, NULL, NULL, NULL, NULL, | ||
241 | &attrs->ia_atime, &attrs->ia_mtime, NULL, | ||
242 | NULL, NULL); | ||
243 | if(err != 0) return(err); | ||
244 | } | ||
245 | return(0); | ||
246 | } | ||
247 | |||
248 | int make_symlink(const char *from, const char *to) | ||
249 | { | ||
250 | int err; | ||
251 | |||
252 | err = symlink(to, from); | ||
253 | if(err) return(-errno); | ||
254 | return(0); | ||
255 | } | ||
256 | |||
257 | int unlink_file(const char *file) | ||
258 | { | ||
259 | int err; | ||
260 | |||
261 | err = unlink(file); | ||
262 | if(err) return(-errno); | ||
263 | return(0); | ||
264 | } | ||
265 | |||
266 | int do_mkdir(const char *file, int mode) | ||
267 | { | ||
268 | int err; | ||
269 | |||
270 | err = mkdir(file, mode); | ||
271 | if(err) return(-errno); | ||
272 | return(0); | ||
273 | } | ||
274 | |||
275 | int do_rmdir(const char *file) | ||
276 | { | ||
277 | int err; | ||
278 | |||
279 | err = rmdir(file); | ||
280 | if(err) return(-errno); | ||
281 | return(0); | ||
282 | } | ||
283 | |||
284 | int do_mknod(const char *file, int mode, int dev) | ||
285 | { | ||
286 | int err; | ||
287 | |||
288 | err = mknod(file, mode, dev); | ||
289 | if(err) return(-errno); | ||
290 | return(0); | ||
291 | } | ||
292 | |||
293 | int link_file(const char *to, const char *from) | ||
294 | { | ||
295 | int err; | ||
296 | |||
297 | err = link(to, from); | ||
298 | if(err) return(-errno); | ||
299 | return(0); | ||
300 | } | ||
301 | |||
302 | int do_readlink(char *file, char *buf, int size) | ||
303 | { | ||
304 | int n; | ||
305 | |||
306 | n = readlink(file, buf, size); | ||
307 | if(n < 0) | ||
308 | return(-errno); | ||
309 | if(n < size) | ||
310 | buf[n] = '\0'; | ||
311 | return(n); | ||
312 | } | ||
313 | |||
314 | int rename_file(char *from, char *to) | ||
315 | { | ||
316 | int err; | ||
317 | |||
318 | err = rename(from, to); | ||
319 | if(err < 0) return(-errno); | ||
320 | return(0); | ||
321 | } | ||
322 | |||
323 | int do_statfs(char *root, long *bsize_out, long long *blocks_out, | ||
324 | long long *bfree_out, long long *bavail_out, | ||
325 | long long *files_out, long long *ffree_out, | ||
326 | void *fsid_out, int fsid_size, long *namelen_out, | ||
327 | long *spare_out) | ||
328 | { | ||
329 | struct statfs64 buf; | ||
330 | int err; | ||
331 | |||
332 | err = statfs64(root, &buf); | ||
333 | if(err < 0) return(-errno); | ||
334 | *bsize_out = buf.f_bsize; | ||
335 | *blocks_out = buf.f_blocks; | ||
336 | *bfree_out = buf.f_bfree; | ||
337 | *bavail_out = buf.f_bavail; | ||
338 | *files_out = buf.f_files; | ||
339 | *ffree_out = buf.f_ffree; | ||
340 | memcpy(fsid_out, &buf.f_fsid, | ||
341 | sizeof(buf.f_fsid) > fsid_size ? fsid_size : | ||
342 | sizeof(buf.f_fsid)); | ||
343 | *namelen_out = buf.f_namelen; | ||
344 | spare_out[0] = buf.f_spare[0]; | ||
345 | spare_out[1] = buf.f_spare[1]; | ||
346 | spare_out[2] = buf.f_spare[2]; | ||
347 | spare_out[3] = buf.f_spare[3]; | ||
348 | spare_out[4] = buf.f_spare[4]; | ||
349 | spare_out[5] = buf.f_spare[5]; | ||
350 | return(0); | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * Overrides for Emacs so that we follow Linus's tabbing style. | ||
355 | * Emacs will notice this stuff at the end of the file and automatically | ||
356 | * adjust the settings for this buffer only. This must remain at the end | ||
357 | * of the file. | ||
358 | * --------------------------------------------------------------------------- | ||
359 | * Local variables: | ||
360 | * c-file-style: "linux" | ||
361 | * End: | ||
362 | */ | ||