• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <sys/statfs.h>
26 #include <sys/uio.h>
27 #include <dirent.h>
28 #include <ctype.h>
29 
30 #include <private/android_filesystem_config.h>
31 
32 #include "fuse.h"
33 
34 /* README
35  *
36  * What is this?
37  *
38  * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
39  * directory permissions (all files are given fixed owner, group, and
40  * permissions at creation, owner, group, and permissions are not
41  * changeable, symlinks and hardlinks are not createable, etc.
42  *
43  * usage:  sdcard <path> <uid> <gid>
44  *
45  * It must be run as root, but will change to uid/gid as soon as it
46  * mounts a filesystem on /storage/sdcard.  It will refuse to run if uid or
47  * gid are zero.
48  *
49  *
50  * Things I believe to be true:
51  *
52  * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
53  * CREAT) must bump that node's refcount
54  * - don't forget that FORGET can forget multiple references (req->nlookup)
55  * - if an op that returns a fuse_entry fails writing the reply to the
56  * kernel, you must rollback the refcount to reflect the reference the
57  * kernel did not actually acquire
58  *
59  */
60 
61 #define FUSE_TRACE 0
62 
63 #if FUSE_TRACE
64 #define TRACE(x...) fprintf(stderr,x)
65 #else
66 #define TRACE(x...) do {} while (0)
67 #endif
68 
69 #define ERROR(x...) fprintf(stderr,x)
70 
71 #define FUSE_UNKNOWN_INO 0xffffffff
72 
73 #define MOUNT_POINT "/storage/sdcard0"
74 
75 struct handle {
76     struct node *node;
77     int fd;
78 };
79 
80 struct dirhandle {
81     struct node *node;
82     DIR *d;
83 };
84 
85 struct node {
86     __u64 nid;
87     __u64 gen;
88 
89     struct node *next;          /* per-dir sibling list */
90     struct node *child;         /* first contained file by this dir */
91     struct node *all;           /* global node list */
92     struct node *parent;        /* containing directory */
93 
94     __u32 refcount;
95     __u32 namelen;
96 
97     char *name;
98     /* If non-null, this is the real name of the file in the underlying storage.
99      * This may differ from the field "name" only by case.
100      * strlen(actual_name) will always equal strlen(name), so it is safe to use
101      * namelen for both fields.
102      */
103     char *actual_name;
104 };
105 
106 struct fuse {
107     __u64 next_generation;
108     __u64 next_node_id;
109 
110     int fd;
111 
112     struct node *all;
113 
114     struct node root;
115     char rootpath[1024];
116 };
117 
118 static unsigned uid = -1;
119 static unsigned gid = -1;
120 
121 #define PATH_BUFFER_SIZE 1024
122 
123 #define NO_CASE_SENSITIVE_MATCH 0
124 #define CASE_SENSITIVE_MATCH 1
125 
126 /*
127  * Get the real-life absolute path to a node.
128  *   node: start at this node
129  *   buf: storage for returned string
130  *   name: append this string to path if set
131  */
do_node_get_path(struct node * node,char * buf,const char * name,int match_case_insensitive)132 char *do_node_get_path(struct node *node, char *buf, const char *name, int match_case_insensitive)
133 {
134     struct node *in_node = node;
135     const char *in_name = name;
136     char *out = buf + PATH_BUFFER_SIZE - 1;
137     int len;
138     out[0] = 0;
139 
140     if (name) {
141         len = strlen(name);
142         goto start;
143     }
144 
145     while (node) {
146         name = (node->actual_name ? node->actual_name : node->name);
147         len = node->namelen;
148         node = node->parent;
149     start:
150         if ((len + 1) > (out - buf))
151             return 0;
152         out -= len;
153         memcpy(out, name, len);
154         /* avoid double slash at beginning of path */
155         if (out[0] != '/') {
156             out --;
157             out[0] = '/';
158         }
159     }
160 
161     /* If we are searching for a file within node (rather than computing node's path)
162      * and fail, then we need to look for a case insensitive match.
163      */
164     if (in_name && match_case_insensitive && access(out, F_OK) != 0) {
165         char *path, buffer[PATH_BUFFER_SIZE];
166         DIR* dir;
167         struct dirent* entry;
168         path = do_node_get_path(in_node, buffer, NULL, NO_CASE_SENSITIVE_MATCH);
169         dir = opendir(path);
170         if (!dir) {
171             ERROR("opendir %s failed: %s", path, strerror(errno));
172             return out;
173         }
174 
175         while ((entry = readdir(dir))) {
176             if (!strcasecmp(entry->d_name, in_name)) {
177                 /* we have a match - replace the name */
178                 len = strlen(in_name);
179                 memcpy(buf + PATH_BUFFER_SIZE - len - 1, entry->d_name, len);
180                 break;
181             }
182         }
183         closedir(dir);
184     }
185 
186    return out;
187 }
188 
node_get_path(struct node * node,char * buf,const char * name)189 char *node_get_path(struct node *node, char *buf, const char *name)
190 {
191     /* We look for case insensitive matches by default */
192     return do_node_get_path(node, buf, name, CASE_SENSITIVE_MATCH);
193 }
194 
attr_from_stat(struct fuse_attr * attr,struct stat * s)195 void attr_from_stat(struct fuse_attr *attr, struct stat *s)
196 {
197     attr->ino = s->st_ino;
198     attr->size = s->st_size;
199     attr->blocks = s->st_blocks;
200     attr->atime = s->st_atime;
201     attr->mtime = s->st_mtime;
202     attr->ctime = s->st_ctime;
203     attr->atimensec = s->st_atime_nsec;
204     attr->mtimensec = s->st_mtime_nsec;
205     attr->ctimensec = s->st_ctime_nsec;
206     attr->mode = s->st_mode;
207     attr->nlink = s->st_nlink;
208 
209         /* force permissions to something reasonable:
210          * world readable
211          * writable by the sdcard group
212          */
213     if (attr->mode & 0100) {
214         attr->mode = (attr->mode & (~0777)) | 0775;
215     } else {
216         attr->mode = (attr->mode & (~0777)) | 0664;
217     }
218 
219         /* all files owned by root.sdcard */
220     attr->uid = 0;
221     attr->gid = AID_SDCARD_RW;
222 }
223 
node_get_attr(struct node * node,struct fuse_attr * attr)224 int node_get_attr(struct node *node, struct fuse_attr *attr)
225 {
226     int res;
227     struct stat s;
228     char *path, buffer[PATH_BUFFER_SIZE];
229 
230     path = node_get_path(node, buffer, 0);
231     res = lstat(path, &s);
232     if (res < 0) {
233         ERROR("lstat('%s') errno %d\n", path, errno);
234         return -1;
235     }
236 
237     attr_from_stat(attr, &s);
238     attr->ino = node->nid;
239 
240     return 0;
241 }
242 
add_node_to_parent(struct node * node,struct node * parent)243 static void add_node_to_parent(struct node *node, struct node *parent) {
244     node->parent = parent;
245     node->next = parent->child;
246     parent->child = node;
247     parent->refcount++;
248 }
249 
250 /* Check to see if our parent directory already has a file with a name
251  * that differs only by case.  If we find one, store it in the actual_name
252  * field so node_get_path will map it to this file in the underlying storage.
253  */
node_find_actual_name(struct node * node)254 static void node_find_actual_name(struct node *node)
255 {
256     char *path, buffer[PATH_BUFFER_SIZE];
257     const char *node_name = node->name;
258     DIR* dir;
259     struct dirent* entry;
260 
261     if (!node->parent) return;
262 
263     path = node_get_path(node->parent, buffer, 0);
264     dir = opendir(path);
265     if (!dir) {
266         ERROR("opendir %s failed: %s", path, strerror(errno));
267         return;
268     }
269 
270     while ((entry = readdir(dir))) {
271         const char *test_name = entry->d_name;
272         if (strcmp(test_name, node_name) && !strcasecmp(test_name, node_name)) {
273             /* we have a match - differs but only by case */
274             node->actual_name = strdup(test_name);
275             if (!node->actual_name) {
276                 ERROR("strdup failed - out of memory\n");
277                 exit(1);
278             }
279             break;
280         }
281     }
282     closedir(dir);
283 }
284 
node_create(struct node * parent,const char * name,__u64 nid,__u64 gen)285 struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
286 {
287     struct node *node;
288     int namelen = strlen(name);
289 
290     node = calloc(1, sizeof(struct node));
291     if (node == 0) {
292         return 0;
293     }
294     node->name = malloc(namelen + 1);
295     if (node->name == 0) {
296         free(node);
297         return 0;
298     }
299 
300     node->nid = nid;
301     node->gen = gen;
302     add_node_to_parent(node, parent);
303     memcpy(node->name, name, namelen + 1);
304     node->namelen = namelen;
305     node_find_actual_name(node);
306     return node;
307 }
308 
rename_node(struct node * node,const char * name)309 static char *rename_node(struct node *node, const char *name)
310 {
311     node->namelen = strlen(name);
312     char *newname = realloc(node->name, node->namelen + 1);
313     if (newname == 0)
314         return 0;
315     node->name = newname;
316     memcpy(node->name, name, node->namelen + 1);
317     node_find_actual_name(node);
318     return node->name;
319 }
320 
fuse_init(struct fuse * fuse,int fd,const char * path)321 void fuse_init(struct fuse *fuse, int fd, const char *path)
322 {
323     fuse->fd = fd;
324     fuse->next_node_id = 2;
325     fuse->next_generation = 0;
326 
327     fuse->all = &fuse->root;
328 
329     memset(&fuse->root, 0, sizeof(fuse->root));
330     fuse->root.nid = FUSE_ROOT_ID; /* 1 */
331     fuse->root.refcount = 2;
332     rename_node(&fuse->root, path);
333 }
334 
id_to_ptr(__u64 nid)335 static inline void *id_to_ptr(__u64 nid)
336 {
337     return (void *) nid;
338 }
339 
ptr_to_id(void * ptr)340 static inline __u64 ptr_to_id(void *ptr)
341 {
342     return (__u64) ptr;
343 }
344 
345 
lookup_by_inode(struct fuse * fuse,__u64 nid)346 struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
347 {
348     if (nid == FUSE_ROOT_ID) {
349         return &fuse->root;
350     } else {
351         return id_to_ptr(nid);
352     }
353 }
354 
lookup_child_by_name(struct node * node,const char * name)355 struct node *lookup_child_by_name(struct node *node, const char *name)
356 {
357     for (node = node->child; node; node = node->next) {
358         if (!strcmp(name, node->name)) {
359             return node;
360         }
361     }
362     return 0;
363 }
364 
lookup_child_by_inode(struct node * node,__u64 nid)365 struct node *lookup_child_by_inode(struct node *node, __u64 nid)
366 {
367     for (node = node->child; node; node = node->next) {
368         if (node->nid == nid) {
369             return node;
370         }
371     }
372     return 0;
373 }
374 
dec_refcount(struct node * node)375 static void dec_refcount(struct node *node) {
376     if (node->refcount > 0) {
377         node->refcount--;
378         TRACE("dec_refcount %p(%s) -> %d\n", node, node->name, node->refcount);
379     } else {
380         ERROR("Zero refcnt %p\n", node);
381     }
382  }
383 
remove_child(struct node * parent,__u64 nid)384 static struct node *remove_child(struct node *parent, __u64 nid)
385 {
386     struct node *prev = 0;
387     struct node *node;
388 
389     for (node = parent->child; node; node = node->next) {
390         if (node->nid == nid) {
391             if (prev) {
392                 prev->next = node->next;
393             } else {
394                 parent->child = node->next;
395             }
396             node->next = 0;
397             node->parent = 0;
398             dec_refcount(parent);
399             return node;
400         }
401         prev = node;
402     }
403     return 0;
404 }
405 
node_lookup(struct fuse * fuse,struct node * parent,const char * name,struct fuse_attr * attr)406 struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
407                          struct fuse_attr *attr)
408 {
409     int res;
410     struct stat s;
411     char *path, buffer[PATH_BUFFER_SIZE];
412     struct node *node;
413 
414     path = node_get_path(parent, buffer, name);
415         /* XXX error? */
416 
417     res = lstat(path, &s);
418     if (res < 0)
419         return 0;
420 
421     node = lookup_child_by_name(parent, name);
422     if (!node) {
423         node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++);
424         if (!node)
425             return 0;
426         node->nid = ptr_to_id(node);
427         node->all = fuse->all;
428         fuse->all = node;
429     }
430 
431     attr_from_stat(attr, &s);
432     attr->ino = node->nid;
433 
434     return node;
435 }
436 
node_release(struct node * node)437 void node_release(struct node *node)
438 {
439     TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
440     dec_refcount(node);
441     if (node->refcount == 0) {
442         if (node->parent->child == node) {
443             node->parent->child = node->parent->child->next;
444         } else {
445             struct node *node2;
446 
447             node2 = node->parent->child;
448             while (node2->next != node)
449                 node2 = node2->next;
450             node2->next = node->next;
451         }
452 
453         TRACE("DESTROY %p (%s)\n", node, node->name);
454 
455         node_release(node->parent);
456 
457         node->parent = 0;
458         node->next = 0;
459 
460             /* TODO: remove debugging - poison memory */
461         memset(node->name, 0xef, node->namelen);
462         free(node->name);
463         free(node->actual_name);
464         memset(node, 0xfc, sizeof(*node));
465         free(node);
466     }
467 }
468 
fuse_status(struct fuse * fuse,__u64 unique,int err)469 void fuse_status(struct fuse *fuse, __u64 unique, int err)
470 {
471     struct fuse_out_header hdr;
472     hdr.len = sizeof(hdr);
473     hdr.error = err;
474     hdr.unique = unique;
475     if (err) {
476 //        ERROR("*** %d ***\n", err);
477     }
478     write(fuse->fd, &hdr, sizeof(hdr));
479 }
480 
fuse_reply(struct fuse * fuse,__u64 unique,void * data,int len)481 void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
482 {
483     struct fuse_out_header hdr;
484     struct iovec vec[2];
485     int res;
486 
487     hdr.len = len + sizeof(hdr);
488     hdr.error = 0;
489     hdr.unique = unique;
490 
491     vec[0].iov_base = &hdr;
492     vec[0].iov_len = sizeof(hdr);
493     vec[1].iov_base = data;
494     vec[1].iov_len = len;
495 
496     res = writev(fuse->fd, vec, 2);
497     if (res < 0) {
498         ERROR("*** REPLY FAILED *** %d\n", errno);
499     }
500 }
501 
lookup_entry(struct fuse * fuse,struct node * node,const char * name,__u64 unique)502 void lookup_entry(struct fuse *fuse, struct node *node,
503                   const char *name, __u64 unique)
504 {
505     struct fuse_entry_out out;
506 
507     memset(&out, 0, sizeof(out));
508 
509     node = node_lookup(fuse, node, name, &out.attr);
510     if (!node) {
511         fuse_status(fuse, unique, -ENOENT);
512         return;
513     }
514 
515     node->refcount++;
516 //    fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
517     out.nodeid = node->nid;
518     out.generation = node->gen;
519     out.entry_valid = 10;
520     out.attr_valid = 10;
521 
522     fuse_reply(fuse, unique, &out, sizeof(out));
523 }
524 
handle_fuse_request(struct fuse * fuse,struct fuse_in_header * hdr,void * data,unsigned len)525 void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
526 {
527     struct node *node;
528 
529     if ((len < sizeof(*hdr)) || (hdr->len != len)) {
530         ERROR("malformed header\n");
531         return;
532     }
533 
534     len -= hdr->len;
535 
536     if (hdr->nodeid) {
537         node = lookup_by_inode(fuse, hdr->nodeid);
538         if (!node) {
539             fuse_status(fuse, hdr->unique, -ENOENT);
540             return;
541         }
542     } else {
543         node = 0;
544     }
545 
546     switch (hdr->opcode) {
547     case FUSE_LOOKUP: { /* bytez[] -> entry_out */
548         TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
549         lookup_entry(fuse, node, (char*) data, hdr->unique);
550         return;
551     }
552     case FUSE_FORGET: {
553         struct fuse_forget_in *req = data;
554         TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
555             /* no reply */
556         while (req->nlookup--)
557             node_release(node);
558         return;
559     }
560     case FUSE_GETATTR: { /* getattr_in -> attr_out */
561         struct fuse_getattr_in *req = data;
562         struct fuse_attr_out out;
563 
564         TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
565 
566         memset(&out, 0, sizeof(out));
567         node_get_attr(node, &out.attr);
568         out.attr_valid = 10;
569 
570         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
571         return;
572     }
573     case FUSE_SETATTR: { /* setattr_in -> attr_out */
574         struct fuse_setattr_in *req = data;
575         struct fuse_attr_out out;
576         char *path, buffer[PATH_BUFFER_SIZE];
577         int res = 0;
578         struct timespec times[2];
579 
580         TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
581               req->fh, hdr->nodeid, req->valid);
582 
583         /* XXX: incomplete implementation on purpose.   chmod/chown
584          * should NEVER be implemented.*/
585 
586         path = node_get_path(node, buffer, 0);
587         if (req->valid & FATTR_SIZE)
588             res = truncate(path, req->size);
589         if (res)
590             goto getout;
591 
592         /* Handle changing atime and mtime.  If FATTR_ATIME_and FATTR_ATIME_NOW
593          * are both set, then set it to the current time.  Else, set it to the
594          * time specified in the request.  Same goes for mtime.  Use utimensat(2)
595          * as it allows ATIME and MTIME to be changed independently, and has
596          * nanosecond resolution which fuse also has.
597          */
598         if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
599             times[0].tv_nsec = UTIME_OMIT;
600             times[1].tv_nsec = UTIME_OMIT;
601             if (req->valid & FATTR_ATIME) {
602                 if (req->valid & FATTR_ATIME_NOW) {
603                   times[0].tv_nsec = UTIME_NOW;
604                 } else {
605                   times[0].tv_sec = req->atime;
606                   times[0].tv_nsec = req->atimensec;
607                 }
608             }
609             if (req->valid & FATTR_MTIME) {
610                 if (req->valid & FATTR_MTIME_NOW) {
611                   times[1].tv_nsec = UTIME_NOW;
612                 } else {
613                   times[1].tv_sec = req->mtime;
614                   times[1].tv_nsec = req->mtimensec;
615                 }
616             }
617             TRACE("Calling utimensat on %s with atime %ld, mtime=%ld\n", path, times[0].tv_sec, times[1].tv_sec);
618             res = utimensat(-1, path, times, 0);
619         }
620 
621         getout:
622         memset(&out, 0, sizeof(out));
623         node_get_attr(node, &out.attr);
624         out.attr_valid = 10;
625 
626         if (res)
627             fuse_status(fuse, hdr->unique, -errno);
628         else
629             fuse_reply(fuse, hdr->unique, &out, sizeof(out));
630         return;
631     }
632 //    case FUSE_READLINK:
633 //    case FUSE_SYMLINK:
634     case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
635         struct fuse_mknod_in *req = data;
636         char *path, buffer[PATH_BUFFER_SIZE];
637         char *name = ((char*) data) + sizeof(*req);
638         int res;
639 
640         TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
641         path = node_get_path(node, buffer, name);
642 
643         req->mode = (req->mode & (~0777)) | 0664;
644         res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
645         if (res < 0) {
646             fuse_status(fuse, hdr->unique, -errno);
647         } else {
648             lookup_entry(fuse, node, name, hdr->unique);
649         }
650         return;
651     }
652     case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
653         struct fuse_mkdir_in *req = data;
654         struct fuse_entry_out out;
655         char *path, buffer[PATH_BUFFER_SIZE];
656         char *name = ((char*) data) + sizeof(*req);
657         int res;
658 
659         TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
660         path = node_get_path(node, buffer, name);
661 
662         req->mode = (req->mode & (~0777)) | 0775;
663         res = mkdir(path, req->mode);
664         if (res < 0) {
665             fuse_status(fuse, hdr->unique, -errno);
666         } else {
667             lookup_entry(fuse, node, name, hdr->unique);
668         }
669         return;
670     }
671     case FUSE_UNLINK: { /* bytez[] -> */
672         char *path, buffer[PATH_BUFFER_SIZE];
673         int res;
674         TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
675         path = node_get_path(node, buffer, (char*) data);
676         res = unlink(path);
677         fuse_status(fuse, hdr->unique, res ? -errno : 0);
678         return;
679     }
680     case FUSE_RMDIR: { /* bytez[] -> */
681         char *path, buffer[PATH_BUFFER_SIZE];
682         int res;
683         TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
684         path = node_get_path(node, buffer, (char*) data);
685         res = rmdir(path);
686         fuse_status(fuse, hdr->unique, res ? -errno : 0);
687         return;
688     }
689     case FUSE_RENAME: { /* rename_in, oldname, newname ->  */
690         struct fuse_rename_in *req = data;
691         char *oldname = ((char*) data) + sizeof(*req);
692         char *newname = oldname + strlen(oldname) + 1;
693         char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
694         char *newpath, newbuffer[PATH_BUFFER_SIZE];
695         struct node *target;
696         struct node *newparent;
697         int res;
698 
699         TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
700 
701         target = lookup_child_by_name(node, oldname);
702         if (!target) {
703             fuse_status(fuse, hdr->unique, -ENOENT);
704             return;
705         }
706         oldpath = node_get_path(node, oldbuffer, oldname);
707 
708         newparent = lookup_by_inode(fuse, req->newdir);
709         if (!newparent) {
710             fuse_status(fuse, hdr->unique, -ENOENT);
711             return;
712         }
713         if (newparent == node) {
714             /* Special case for renaming a file where destination
715              * is same path differing only by case.
716              * In this case we don't want to look for a case insensitive match.
717              * This allows commands like "mv foo FOO" to work as expected.
718              */
719             newpath = do_node_get_path(newparent, newbuffer, newname, NO_CASE_SENSITIVE_MATCH);
720         } else {
721             newpath = node_get_path(newparent, newbuffer, newname);
722         }
723 
724         if (!remove_child(node, target->nid)) {
725             ERROR("RENAME remove_child not found");
726             fuse_status(fuse, hdr->unique, -ENOENT);
727             return;
728         }
729         if (!rename_node(target, newname)) {
730             fuse_status(fuse, hdr->unique, -ENOMEM);
731             return;
732         }
733         add_node_to_parent(target, newparent);
734 
735         res = rename(oldpath, newpath);
736         TRACE("RENAME result %d\n", res);
737 
738         fuse_status(fuse, hdr->unique, res ? -errno : 0);
739         return;
740     }
741 //    case FUSE_LINK:
742     case FUSE_OPEN: { /* open_in -> open_out */
743         struct fuse_open_in *req = data;
744         struct fuse_open_out out;
745         char *path, buffer[PATH_BUFFER_SIZE];
746         struct handle *h;
747 
748         h = malloc(sizeof(*h));
749         if (!h) {
750             fuse_status(fuse, hdr->unique, -ENOMEM);
751             return;
752         }
753 
754         path = node_get_path(node, buffer, 0);
755         TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
756         h->fd = open(path, req->flags);
757         if (h->fd < 0) {
758             ERROR("ERROR\n");
759             fuse_status(fuse, hdr->unique, -errno);
760             free(h);
761             return;
762         }
763         out.fh = ptr_to_id(h);
764         out.open_flags = 0;
765         out.padding = 0;
766         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
767         return;
768     }
769     case FUSE_READ: { /* read_in -> byte[] */
770         char buffer[128 * 1024];
771         struct fuse_read_in *req = data;
772         struct handle *h = id_to_ptr(req->fh);
773         int res;
774         TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
775         if (req->size > sizeof(buffer)) {
776             fuse_status(fuse, hdr->unique, -EINVAL);
777             return;
778         }
779         res = pread64(h->fd, buffer, req->size, req->offset);
780         if (res < 0) {
781             fuse_status(fuse, hdr->unique, -errno);
782             return;
783         }
784         fuse_reply(fuse, hdr->unique, buffer, res);
785         return;
786     }
787     case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
788         struct fuse_write_in *req = data;
789         struct fuse_write_out out;
790         struct handle *h = id_to_ptr(req->fh);
791         int res;
792         TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
793         res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
794         if (res < 0) {
795             fuse_status(fuse, hdr->unique, -errno);
796             return;
797         }
798         out.size = res;
799         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
800         goto oops;
801     }
802     case FUSE_STATFS: { /* getattr_in -> attr_out */
803         struct statfs stat;
804         struct fuse_statfs_out out;
805         int res;
806 
807         TRACE("STATFS\n");
808 
809         if (statfs(fuse->root.name, &stat)) {
810             fuse_status(fuse, hdr->unique, -errno);
811             return;
812         }
813 
814         memset(&out, 0, sizeof(out));
815         out.st.blocks = stat.f_blocks;
816         out.st.bfree = stat.f_bfree;
817         out.st.bavail = stat.f_bavail;
818         out.st.files = stat.f_files;
819         out.st.ffree = stat.f_ffree;
820         out.st.bsize = stat.f_bsize;
821         out.st.namelen = stat.f_namelen;
822         out.st.frsize = stat.f_frsize;
823         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
824         return;
825     }
826     case FUSE_RELEASE: { /* release_in -> */
827         struct fuse_release_in *req = data;
828         struct handle *h = id_to_ptr(req->fh);
829         TRACE("RELEASE %p(%d)\n", h, h->fd);
830         close(h->fd);
831         free(h);
832         fuse_status(fuse, hdr->unique, 0);
833         return;
834     }
835 //    case FUSE_FSYNC:
836 //    case FUSE_SETXATTR:
837 //    case FUSE_GETXATTR:
838 //    case FUSE_LISTXATTR:
839 //    case FUSE_REMOVEXATTR:
840     case FUSE_FLUSH:
841         fuse_status(fuse, hdr->unique, 0);
842         return;
843     case FUSE_OPENDIR: { /* open_in -> open_out */
844         struct fuse_open_in *req = data;
845         struct fuse_open_out out;
846         char *path, buffer[PATH_BUFFER_SIZE];
847         struct dirhandle *h;
848 
849         h = malloc(sizeof(*h));
850         if (!h) {
851             fuse_status(fuse, hdr->unique, -ENOMEM);
852             return;
853         }
854 
855         path = node_get_path(node, buffer, 0);
856         TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
857         h->d = opendir(path);
858         if (h->d == 0) {
859             ERROR("ERROR\n");
860             fuse_status(fuse, hdr->unique, -errno);
861             free(h);
862             return;
863         }
864         out.fh = ptr_to_id(h);
865         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
866         return;
867     }
868     case FUSE_READDIR: {
869         struct fuse_read_in *req = data;
870         char buffer[8192];
871         struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
872         struct dirent *de;
873         struct dirhandle *h = id_to_ptr(req->fh);
874         TRACE("READDIR %p\n", h);
875         if (req->offset == 0) {
876             /* rewinddir() might have been called above us, so rewind here too */
877             TRACE("calling rewinddir()\n");
878             rewinddir(h->d);
879         }
880         de = readdir(h->d);
881         if (!de) {
882             fuse_status(fuse, hdr->unique, 0);
883             return;
884         }
885         fde->ino = FUSE_UNKNOWN_INO;
886         /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
887         fde->off = req->offset + 1;
888         fde->type = de->d_type;
889         fde->namelen = strlen(de->d_name);
890         memcpy(fde->name, de->d_name, fde->namelen + 1);
891         fuse_reply(fuse, hdr->unique, fde,
892                    FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
893         return;
894     }
895     case FUSE_RELEASEDIR: { /* release_in -> */
896         struct fuse_release_in *req = data;
897         struct dirhandle *h = id_to_ptr(req->fh);
898         TRACE("RELEASEDIR %p\n",h);
899         closedir(h->d);
900         free(h);
901         fuse_status(fuse, hdr->unique, 0);
902         return;
903     }
904 //    case FUSE_FSYNCDIR:
905     case FUSE_INIT: { /* init_in -> init_out */
906         struct fuse_init_in *req = data;
907         struct fuse_init_out out;
908 
909         TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
910                 req->major, req->minor, req->max_readahead, req->flags);
911 
912         out.major = FUSE_KERNEL_VERSION;
913         out.minor = FUSE_KERNEL_MINOR_VERSION;
914         out.max_readahead = req->max_readahead;
915         out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
916         out.max_background = 32;
917         out.congestion_threshold = 32;
918         out.max_write = 256 * 1024;
919 
920         fuse_reply(fuse, hdr->unique, &out, sizeof(out));
921         return;
922     }
923     default: {
924         struct fuse_out_header h;
925         ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
926                 hdr->opcode, hdr->unique, hdr->nodeid);
927 
928         oops:
929         h.len = sizeof(h);
930         h.error = -ENOSYS;
931         h.unique = hdr->unique;
932         write(fuse->fd, &h, sizeof(h));
933         break;
934     }
935     }
936 }
937 
handle_fuse_requests(struct fuse * fuse)938 void handle_fuse_requests(struct fuse *fuse)
939 {
940     unsigned char req[256 * 1024 + 128];
941     int len;
942 
943     for (;;) {
944         len = read(fuse->fd, req, sizeof(req));
945         if (len < 0) {
946             if (errno == EINTR)
947                 continue;
948             ERROR("handle_fuse_requests: errno=%d\n", errno);
949             return;
950         }
951         handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
952     }
953 }
954 
usage()955 static int usage()
956 {
957     ERROR("usage: sdcard [-l -f] <path> <uid> <gid>\n\n\t-l force file names to lower case when creating new files\n\t-f fix up file system before starting (repairs bad file name case and group ownership)\n");
958     return -1;
959 }
960 
main(int argc,char ** argv)961 int main(int argc, char **argv)
962 {
963     struct fuse fuse;
964     char opts[256];
965     int fd;
966     int res;
967     const char *path = NULL;
968     int i;
969 
970     for (i = 1; i < argc; i++) {
971         char* arg = argv[i];
972         if (!path)
973             path = arg;
974         else if (uid == -1)
975             uid = strtoul(arg, 0, 10);
976         else if (gid == -1)
977             gid = strtoul(arg, 0, 10);
978         else {
979             ERROR("too many arguments\n");
980             return usage();
981         }
982     }
983 
984     if (!path) {
985         ERROR("no path specified\n");
986         return usage();
987     }
988     if (uid <= 0 || gid <= 0) {
989         ERROR("uid and gid must be nonzero\n");
990         return usage();
991     }
992 
993         /* cleanup from previous instance, if necessary */
994     umount2(MOUNT_POINT, 2);
995 
996     fd = open("/dev/fuse", O_RDWR);
997     if (fd < 0){
998         ERROR("cannot open fuse device (%d)\n", errno);
999         return -1;
1000     }
1001 
1002     sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
1003             "user_id=%d,group_id=%d", fd, uid, gid);
1004 
1005     res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
1006     if (res < 0) {
1007         ERROR("cannot mount fuse filesystem (%d)\n", errno);
1008         return -1;
1009     }
1010 
1011     if (setgid(gid) < 0) {
1012         ERROR("cannot setgid!\n");
1013         return -1;
1014     }
1015     if (setuid(uid) < 0) {
1016         ERROR("cannot setuid!\n");
1017         return -1;
1018     }
1019 
1020     fuse_init(&fuse, fd, path);
1021 
1022     umask(0);
1023     handle_fuse_requests(&fuse);
1024 
1025     return 0;
1026 }
1027