blob: 5d1960451ccba3139dcfea60218098b5e0c87948 [file] [log] [blame]
Brian Swetland03ee9472010-08-12 18:01:08 -07001/*
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/uio.h>
26#include <dirent.h>
27
28#include "fuse.h"
29
30/* README
31 *
32 * What is this?
33 *
34 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
35 * directory permissions (all files are given fixed owner, group, and
36 * permissions at creation, owner, group, and permissions are not
37 * changeable, symlinks and hardlinks are not createable, etc.
38 *
39 * usage: sdcard <path> <uid> <gid>
40 *
41 * It must be run as root, but will change to uid/gid as soon as it
42 * mounts a filesystem on /sdcard. It will refuse to run if uid or
43 * gid are zero.
44 *
45 *
46 * Things I believe to be true:
47 *
48 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
49 * CREAT) must bump that node's refcount
50 * - don't forget that FORGET can forget multiple references (req->nlookup)
51 * - if an op that returns a fuse_entry fails writing the reply to the
52 * kernel, you must rollback the refcount to reflect the reference the
53 * kernel did not actually acquire
54 *
55 *
56 * Bugs:
57 *
58 * - need to move/rename node on RENAME
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
73struct handle {
74 struct node *node;
75 int fd;
76};
77
78struct dirhandle {
79 struct node *node;
80 DIR *d;
81};
82
83struct node {
84 __u64 nid;
85 __u64 gen;
86
87 struct node *next;
88 struct node *child;
89 struct node *all;
90 struct node *parent;
91
92 __u32 refcount;
93 __u32 namelen;
94
95 char name[1];
96};
97
98struct fuse {
99 __u64 next_generation;
100 __u64 next_node_id;
101
102 int fd;
103
104 struct node *all;
105
106 struct node root;
107 char rootpath[1024];
108};
109
110#define PATH_BUFFER_SIZE 1024
111
112char *node_get_path(struct node *node, char *buf, const char *name)
113{
114 char *out = buf + PATH_BUFFER_SIZE - 1;
115 int len;
116 out[0] = 0;
117
118 if (name) {
119 len = strlen(name);
120 goto start;
121 }
122
123 while (node) {
124 name = node->name;
125 len = node->namelen;
126 node = node->parent;
127 start:
128 if ((len + 1) > (out - buf))
129 return 0;
130 out -= len;
131 memcpy(out, name, len);
132 out --;
133 out[0] = '/';
134 }
135
136 return out;
137}
138
139void attr_from_stat(struct fuse_attr *attr, struct stat *s)
140{
141 attr->ino = s->st_ino;
142 attr->size = s->st_size;
143 attr->blocks = s->st_blocks;
144 /* TODO: time */
145 attr->mode = s->st_mode;
146 attr->nlink = s->st_nlink;
147 /* TODO: uid/gid */
148
149 attr->uid = 1000;
150 attr->gid = 1000;
151}
152
153int node_get_attr(struct node *node, struct fuse_attr *attr)
154{
155 int res;
156 struct stat s;
157 char *path, buffer[PATH_BUFFER_SIZE];
158
159 path = node_get_path(node, buffer, 0);
160 res = lstat(path, &s);
161 if (res < 0) {
162 ERROR("lstat('%s') errno %d\n", path, errno);
163 return -1;
164 }
165
166 attr_from_stat(attr, &s);
167 attr->ino = node->nid;
168
169 return 0;
170}
171
172struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
173{
174 struct node *node;
175 int namelen = strlen(name);
176
177 node = calloc(1, sizeof(struct node) + namelen);
178 if (node == 0) {
179 return 0;
180 }
181
182 node->nid = nid;
183 node->gen = gen;
184 node->parent = parent;
185 node->next = parent->child;
186 parent->child = node;
187 memcpy(node->name, name, namelen + 1);
188 node->namelen = namelen;
189 parent->refcount++;
190
191 return node;
192}
193
194void fuse_init(struct fuse *fuse, int fd, const char *path)
195{
196 fuse->fd = fd;
197 fuse->next_node_id = 2;
198 fuse->next_generation = 0;
199
200 fuse->all = &fuse->root;
201
202 fuse->root.nid = FUSE_ROOT_ID; /* 1 */
203 fuse->root.next = 0;
204 fuse->root.child = 0;
205 fuse->root.parent = 0;
206
207 fuse->root.all = 0;
208 fuse->root.refcount = 2;
209
210 strcpy(fuse->root.name, path);
211 fuse->root.namelen = strlen(fuse->root.name);
212}
213
214static inline void *id_to_ptr(__u64 nid)
215{
216 return (void *) nid;
217}
218
219static inline __u64 ptr_to_id(void *ptr)
220{
221 return (__u64) ptr;
222}
223
224
225struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
226{
227 if (nid == FUSE_ROOT_ID) {
228 return &fuse->root;
229 } else {
230 return id_to_ptr(nid);
231 }
232}
233
234struct node *lookup_child_by_name(struct node *node, const char *name)
235{
236 for (node = node->child; node; node = node->next) {
237 if (!strcmp(name, node->name)) {
238 return node;
239 }
240 }
241 return 0;
242}
243
244struct node *lookup_child_by_inode(struct node *node, __u64 nid)
245{
246 for (node = node->child; node; node = node->next) {
247 if (node->nid == nid) {
248 return node;
249 }
250 }
251 return 0;
252}
253
254struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
255 struct fuse_attr *attr)
256{
257 int res;
258 struct stat s;
259 char *path, buffer[PATH_BUFFER_SIZE];
260 struct node *node;
261
262 path = node_get_path(parent, buffer, name);
263 /* XXX error? */
264
265 res = lstat(path, &s);
266 if (res < 0)
267 return 0;
268
269 node = lookup_child_by_name(parent, name);
270 if (!node) {
271 node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++);
272 if (!node)
273 return 0;
274 node->nid = ptr_to_id(node);
275 node->all = fuse->all;
276 fuse->all = node;
277 }
278
279 attr_from_stat(attr, &s);
280 attr->ino = node->nid;
281
282 return node;
283}
284
285void node_release(struct node *node)
286{
287 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
288 node->refcount--;
289 if (node->refcount == 0) {
290 if (node->parent->child == node) {
291 node->parent->child = node->parent->child->next;
292 } else {
293 struct node *node2;
294
295 node2 = node->parent->child;
296 while (node2->next != node)
297 node2 = node2->next;
298 node2->next = node->next;
299 }
300
301 TRACE("DESTROY %p (%s)\n", node, node->name);
302
303 node_release(node->parent);
304
305 node->parent = 0;
306 node->next = 0;
307
308 /* TODO: remove debugging - poison memory */
309 memset(node, 0xef, sizeof(*node) + strlen(node->name));
310
311 free(node);
312 }
313}
314
315void fuse_status(struct fuse *fuse, __u64 unique, int err)
316{
317 struct fuse_out_header hdr;
318 hdr.len = sizeof(hdr);
319 hdr.error = err;
320 hdr.unique = unique;
321 if (err) {
322// ERROR("*** %d ***\n", err);
323 }
324 write(fuse->fd, &hdr, sizeof(hdr));
325}
326
327void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
328{
329 struct fuse_out_header hdr;
330 struct iovec vec[2];
331 int res;
332
333 hdr.len = len + sizeof(hdr);
334 hdr.error = 0;
335 hdr.unique = unique;
336
337 vec[0].iov_base = &hdr;
338 vec[0].iov_len = sizeof(hdr);
339 vec[1].iov_base = data;
340 vec[1].iov_len = len;
341
342 res = writev(fuse->fd, vec, 2);
343 if (res < 0) {
344 ERROR("*** REPLY FAILED *** %d\n", errno);
345 }
346}
347
348void lookup_entry(struct fuse *fuse, struct node *node,
349 const char *name, __u64 unique)
350{
351 struct fuse_entry_out out;
352
353 memset(&out, 0, sizeof(out));
354
355 node = node_lookup(fuse, node, name, &out.attr);
356 if (!node) {
357 fuse_status(fuse, unique, -ENOENT);
358 return;
359 }
360
361 node->refcount++;
362// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
363 out.nodeid = node->nid;
364 out.generation = node->gen;
365 out.entry_valid = 10;
366 out.attr_valid = 10;
367
368 fuse_reply(fuse, unique, &out, sizeof(out));
369}
370
371void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
372{
373 struct node *node;
374
375 if ((len < sizeof(*hdr)) || (hdr->len != len)) {
376 ERROR("malformed header\n");
377 return;
378 }
379
380 len -= hdr->len;
381
382 if (hdr->nodeid) {
383 node = lookup_by_inode(fuse, hdr->nodeid);
384 if (!node) {
385 fuse_status(fuse, hdr->unique, -ENOENT);
386 return;
387 }
388 } else {
389 node = 0;
390 }
391
392 switch (hdr->opcode) {
393 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
394 TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
395 lookup_entry(fuse, node, (char*) data, hdr->unique);
396 return;
397 }
398 case FUSE_FORGET: {
399 struct fuse_forget_in *req = data;
400 TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
401 /* no reply */
402 while (req->nlookup--)
403 node_release(node);
404 return;
405 }
406 case FUSE_GETATTR: { /* getattr_in -> attr_out */
407 struct fuse_getattr_in *req = data;
408 struct fuse_attr_out out;
409
410 TRACE("GETATTR flags=%x fh=%llx\n",req->getattr_flags, req->fh);
411
412 memset(&out, 0, sizeof(out));
413 node_get_attr(node, &out.attr);
414 out.attr_valid = 10;
415
416 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
417 return;
418 }
419 case FUSE_SETATTR: { /* setattr_in -> attr_out */
420 struct fuse_setattr_in *req = data;
421 struct fuse_attr_out out;
422 TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
423 req->fh, hdr->nodeid, req->valid);
424
425 /* XXX */
426
427 memset(&out, 0, sizeof(out));
428 node_get_attr(node, &out.attr);
429 out.attr_valid = 10;
430 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
431 return;
432 }
433// case FUSE_READLINK:
434// case FUSE_SYMLINK:
435 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
436 struct fuse_mknod_in *req = data;
437 char *path, buffer[PATH_BUFFER_SIZE];
438 char *name = ((char*) data) + sizeof(*req);
439 int res;
440 TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
441 path = node_get_path(node, buffer, name);
442
443 req->mode = (req->mode & (~0777)) | 0664;
444 res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
445 if (res < 0) {
446 fuse_status(fuse, hdr->unique, -errno);
447 } else {
448 lookup_entry(fuse, node, name, hdr->unique);
449 }
450 return;
451 }
452 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
453 struct fuse_mkdir_in *req = data;
454 struct fuse_entry_out out;
455 char *path, buffer[PATH_BUFFER_SIZE];
456 char *name = ((char*) data) + sizeof(*req);
457 int res;
458 TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
459 path = node_get_path(node, buffer, name);
460
461 req->mode = (req->mode & (~0777)) | 0775;
462 res = mkdir(path, req->mode);
463 if (res < 0) {
464 fuse_status(fuse, hdr->unique, -errno);
465 } else {
466 lookup_entry(fuse, node, name, hdr->unique);
467 }
468 return;
469 }
470 case FUSE_UNLINK: { /* bytez[] -> */
471 char *path, buffer[PATH_BUFFER_SIZE];
472 int res;
473 TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
474 path = node_get_path(node, buffer, (char*) data);
475 res = unlink(path);
476 fuse_status(fuse, hdr->unique, res ? -errno : 0);
477 return;
478 }
479 case FUSE_RMDIR: { /* bytez[] -> */
480 char *path, buffer[PATH_BUFFER_SIZE];
481 int res;
482 TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
483 path = node_get_path(node, buffer, (char*) data);
484 res = rmdir(path);
485 fuse_status(fuse, hdr->unique, res ? -errno : 0);
486 return;
487 }
488 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
489 struct fuse_rename_in *req = data;
490 char *oldname = ((char*) data) + sizeof(*req);
491 char *newname = oldname + strlen(oldname) + 1;
492 char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
493 char *newpath, newbuffer[PATH_BUFFER_SIZE];
494 struct node *newnode;
495 int res;
496
497 newnode = lookup_by_inode(fuse, req->newdir);
498 if (!newnode) {
499 fuse_status(fuse, hdr->unique, -ENOENT);
500 return;
501 }
502
503 oldpath = node_get_path(node, oldbuffer, oldname);
504 newpath = node_get_path(newnode, newbuffer, newname);
505
506 res = rename(oldpath, newpath);
507 fuse_status(fuse, hdr->unique, res ? -errno : 0);
508 return;
509 }
510// case FUSE_LINK:
511 case FUSE_OPEN: { /* open_in -> open_out */
512 struct fuse_open_in *req = data;
513 struct fuse_open_out out;
514 char *path, buffer[PATH_BUFFER_SIZE];
515 struct handle *h;
516
517 h = malloc(sizeof(*h));
518 if (!h) {
519 fuse_status(fuse, hdr->unique, -ENOMEM);
520 return;
521 }
522
523 path = node_get_path(node, buffer, 0);
524 TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
525 h->fd = open(path, req->flags);
526 if (h->fd < 0) {
527 ERROR("ERROR\n");
528 fuse_status(fuse, hdr->unique, errno);
529 free(h);
530 return;
531 }
532 out.fh = ptr_to_id(h);
533 out.open_flags = 0;
534 out.padding = 0;
535 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
536 return;
537 }
538 case FUSE_READ: { /* read_in -> byte[] */
539 char buffer[128 * 1024];
540 struct fuse_read_in *req = data;
541 struct handle *h = id_to_ptr(req->fh);
542 int res;
543 TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
544 if (req->size > sizeof(buffer)) {
545 fuse_status(fuse, hdr->unique, -EINVAL);
546 return;
547 }
548 res = pread(h->fd, buffer, req->size, req->offset);
549 if (res < 0) {
550 fuse_status(fuse, hdr->unique, errno);
551 return;
552 }
553 fuse_reply(fuse, hdr->unique, buffer, res);
554 return;
555 }
556 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
557 struct fuse_write_in *req = data;
558 struct fuse_write_out out;
559 struct handle *h = id_to_ptr(req->fh);
560 int res;
561 TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
562 res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
563 if (res < 0) {
564 fuse_status(fuse, hdr->unique, errno);
565 return;
566 }
567 out.size = res;
568 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
569 goto oops;
570 }
571// case FUSE_STATFS:
572 case FUSE_RELEASE: { /* release_in -> */
573 struct fuse_release_in *req = data;
574 struct handle *h = id_to_ptr(req->fh);
575 TRACE("RELEASE %p(%d)\n", h, h->fd);
576 close(h->fd);
577 free(h);
578 fuse_status(fuse, hdr->unique, 0);
579 return;
580 }
581// case FUSE_FSYNC:
582// case FUSE_SETXATTR:
583// case FUSE_GETXATTR:
584// case FUSE_LISTXATTR:
585// case FUSE_REMOVEXATTR:
586 case FUSE_FLUSH:
587 fuse_status(fuse, hdr->unique, 0);
588 return;
589 case FUSE_OPENDIR: { /* open_in -> open_out */
590 struct fuse_open_in *req = data;
591 struct fuse_open_out out;
592 char *path, buffer[PATH_BUFFER_SIZE];
593 struct dirhandle *h;
594
595 h = malloc(sizeof(*h));
596 if (!h) {
597 fuse_status(fuse, hdr->unique, -ENOMEM);
598 return;
599 }
600
601 path = node_get_path(node, buffer, 0);
602 TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
603 h->d = opendir(path);
604 if (h->d == 0) {
605 ERROR("ERROR\n");
606 fuse_status(fuse, hdr->unique, -errno);
607 free(h);
608 return;
609 }
610 out.fh = ptr_to_id(h);
611 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
612 return;
613 }
614 case FUSE_READDIR: {
615 struct fuse_read_in *req = data;
616 char buffer[8192];
617 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
618 struct dirent *de;
619 struct dirhandle *h = id_to_ptr(req->fh);
620 TRACE("READDIR %p\n", h);
621 de = readdir(h->d);
622 if (!de) {
623 fuse_status(fuse, hdr->unique, 0);
624 return;
625 }
626 fde->ino = FUSE_UNKNOWN_INO;
627 fde->off = 0;
628 fde->type = de->d_type;
629 fde->namelen = strlen(de->d_name);
630 memcpy(fde->name, de->d_name, fde->namelen + 1);
631 fuse_reply(fuse, hdr->unique, fde,
632 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
633 return;
634 }
635 case FUSE_RELEASEDIR: { /* release_in -> */
636 struct fuse_release_in *req = data;
637 struct dirhandle *h = id_to_ptr(req->fh);
638 TRACE("RELEASEDIR %p\n",h);
639 closedir(h->d);
640 free(h);
641 fuse_status(fuse, hdr->unique, 0);
642 return;
643 }
644// case FUSE_FSYNCDIR:
645 case FUSE_INIT: { /* init_in -> init_out */
646 struct fuse_init_in *req = data;
647 struct fuse_init_out out;
648
649 TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
650 req->major, req->minor, req->max_readahead, req->flags);
651
652 out.major = FUSE_KERNEL_VERSION;
653 out.minor = FUSE_KERNEL_MINOR_VERSION;
654 out.max_readahead = req->max_readahead;
655 out.flags = 0;
656 out.max_background = 32;
657 out.congestion_threshold = 32;
658 out.max_write = 256 * 1024;
659
660 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
661 return;
662 }
663 default: {
664 struct fuse_out_header h;
665 ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
666 hdr->opcode, hdr->unique, hdr->nodeid);
667
668 oops:
669 h.len = sizeof(h);
670 h.error = -ENOSYS;
671 h.unique = hdr->unique;
672 write(fuse->fd, &h, sizeof(h));
673 break;
674 }
675 }
676}
677
678void handle_fuse_requests(struct fuse *fuse)
679{
680 unsigned char req[256 * 1024 + 128];
681 int len;
682
683 for (;;) {
684 len = read(fuse->fd, req, 8192);
685 if (len < 0) {
686 if (errno == EINTR)
687 continue;
688 ERROR("handle_fuse_requests: errno=%d\n", errno);
689 return;
690 }
691 handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
692 }
693}
694
695int main(int argc, char **argv)
696{
697 struct fuse fuse;
698 char opts[256];
699 int fd;
700 int res;
701 unsigned uid;
702 unsigned gid;
703 const char *path;
704
705 if (argc != 4) {
706 ERROR("usage: sdcard <path> <uid> <gid>\n");
707 return -1;
708 }
709
710 uid = strtoul(argv[2], 0, 10);
711 gid = strtoul(argv[3], 0, 10);
712 if (!uid || !gid) {
713 ERROR("uid and gid must be nonzero\n");
714 return -1;
715 }
716
717 path = argv[1];
718
719 /* cleanup from previous instance, if necessary */
720 umount2("/sdcard", 2);
721
722 fd = open("/dev/fuse", O_RDWR);
723 if (fd < 0){
724 ERROR("cannot open fuse device (%d)\n", errno);
725 return -1;
726 }
727
728 sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
729 "user_id=%d,group_id=%d", fd, uid, gid);
730
731 res = mount("/dev/fuse", "/sdcard", "fuse", MS_NOSUID | MS_NODEV, opts);
732 if (res < 0) {
733 ERROR("cannot mount fuse filesystem (%d)\n", errno);
734 return -1;
735 }
736
737 if (setgid(gid) < 0) {
738 ERROR("cannot setgid!\n");
739 return -1;
740 }
741 if (setuid(uid) < 0) {
742 ERROR("cannot setuid!\n");
743 return -1;
744 }
745
746 fuse_init(&fuse, fd, path);
747
748 umask(0);
749 handle_fuse_requests(&fuse);
750
751 return 0;
752}