blob: de630f5f67583afefee2d308705955281d5b071e [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>
Mike Lockwood4553b082010-08-16 14:14:44 -040025#include <sys/statfs.h>
Brian Swetland03ee9472010-08-12 18:01:08 -070026#include <sys/uio.h>
27#include <dirent.h>
28
Brian Swetlandb14a2c62010-08-12 18:21:12 -070029#include <private/android_filesystem_config.h>
30
Brian Swetland03ee9472010-08-12 18:01:08 -070031#include "fuse.h"
32
33/* README
34 *
35 * What is this?
36 *
37 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
38 * directory permissions (all files are given fixed owner, group, and
39 * permissions at creation, owner, group, and permissions are not
40 * changeable, symlinks and hardlinks are not createable, etc.
41 *
42 * usage: sdcard <path> <uid> <gid>
43 *
44 * It must be run as root, but will change to uid/gid as soon as it
Mike Lockwood4553b082010-08-16 14:14:44 -040045 * mounts a filesystem on /mnt/sdcard. It will refuse to run if uid or
Brian Swetland03ee9472010-08-12 18:01:08 -070046 * gid are zero.
47 *
48 *
49 * Things I believe to be true:
50 *
51 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
52 * CREAT) must bump that node's refcount
53 * - don't forget that FORGET can forget multiple references (req->nlookup)
54 * - if an op that returns a fuse_entry fails writing the reply to the
55 * kernel, you must rollback the refcount to reflect the reference the
56 * kernel did not actually acquire
57 *
Brian Swetland03ee9472010-08-12 18:01:08 -070058 */
59
60#define FUSE_TRACE 0
61
62#if FUSE_TRACE
63#define TRACE(x...) fprintf(stderr,x)
64#else
65#define TRACE(x...) do {} while (0)
66#endif
67
68#define ERROR(x...) fprintf(stderr,x)
69
70#define FUSE_UNKNOWN_INO 0xffffffff
71
Mike Lockwood4553b082010-08-16 14:14:44 -040072#define MOUNT_POINT "/mnt/sdcard"
73
Brian Swetland03ee9472010-08-12 18:01:08 -070074struct handle {
75 struct node *node;
76 int fd;
77};
78
79struct dirhandle {
80 struct node *node;
81 DIR *d;
82};
83
84struct node {
85 __u64 nid;
86 __u64 gen;
87
Paul Eastham11ccdb32010-10-14 11:04:26 -070088 struct node *next; /* per-dir sibling list */
89 struct node *child; /* first contained file by this dir */
90 struct node *all; /* global node list */
91 struct node *parent; /* containing directory */
Brian Swetland03ee9472010-08-12 18:01:08 -070092
93 __u32 refcount;
94 __u32 namelen;
95
Paul Eastham11ccdb32010-10-14 11:04:26 -070096 char *name;
Brian Swetland03ee9472010-08-12 18:01:08 -070097};
98
99struct fuse {
100 __u64 next_generation;
101 __u64 next_node_id;
102
103 int fd;
104
105 struct node *all;
106
107 struct node root;
108 char rootpath[1024];
109};
110
111#define PATH_BUFFER_SIZE 1024
112
Paul Easthamf43219e2010-09-21 17:14:31 -0700113/*
114 * Get the real-life absolute path to a node.
115 * node: start at this node
116 * buf: storage for returned string
117 * name: append this string to path if set
118 */
Brian Swetland03ee9472010-08-12 18:01:08 -0700119char *node_get_path(struct node *node, char *buf, const char *name)
120{
121 char *out = buf + PATH_BUFFER_SIZE - 1;
122 int len;
123 out[0] = 0;
124
125 if (name) {
126 len = strlen(name);
127 goto start;
128 }
129
130 while (node) {
131 name = node->name;
132 len = node->namelen;
133 node = node->parent;
134 start:
135 if ((len + 1) > (out - buf))
136 return 0;
137 out -= len;
138 memcpy(out, name, len);
139 out --;
140 out[0] = '/';
141 }
142
143 return out;
144}
145
146void attr_from_stat(struct fuse_attr *attr, struct stat *s)
147{
148 attr->ino = s->st_ino;
149 attr->size = s->st_size;
150 attr->blocks = s->st_blocks;
Mike Lockwood4553b082010-08-16 14:14:44 -0400151 attr->atime = s->st_atime;
152 attr->mtime = s->st_mtime;
153 attr->ctime = s->st_ctime;
154 attr->atimensec = s->st_atime_nsec;
155 attr->mtimensec = s->st_mtime_nsec;
156 attr->ctimensec = s->st_ctime_nsec;
Brian Swetland03ee9472010-08-12 18:01:08 -0700157 attr->mode = s->st_mode;
158 attr->nlink = s->st_nlink;
Brian Swetland03ee9472010-08-12 18:01:08 -0700159
Brian Swetlandb14a2c62010-08-12 18:21:12 -0700160 /* force permissions to something reasonable:
161 * world readable
162 * writable by the sdcard group
163 */
164 if (attr->mode & 0100) {
165 attr->mode = (attr->mode & (~0777)) | 0775;
166 } else {
167 attr->mode = (attr->mode & (~0777)) | 0664;
168 }
169
170 /* all files owned by root.sdcard */
171 attr->uid = 0;
172 attr->gid = AID_SDCARD_RW;
Brian Swetland03ee9472010-08-12 18:01:08 -0700173}
174
175int node_get_attr(struct node *node, struct fuse_attr *attr)
176{
177 int res;
178 struct stat s;
179 char *path, buffer[PATH_BUFFER_SIZE];
180
181 path = node_get_path(node, buffer, 0);
182 res = lstat(path, &s);
183 if (res < 0) {
184 ERROR("lstat('%s') errno %d\n", path, errno);
185 return -1;
186 }
187
188 attr_from_stat(attr, &s);
189 attr->ino = node->nid;
190
191 return 0;
192}
193
Paul Eastham11ccdb32010-10-14 11:04:26 -0700194static void add_node_to_parent(struct node *node, struct node *parent) {
195 node->parent = parent;
196 node->next = parent->child;
197 parent->child = node;
198}
199
Brian Swetland03ee9472010-08-12 18:01:08 -0700200struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
201{
202 struct node *node;
203 int namelen = strlen(name);
204
Paul Eastham11ccdb32010-10-14 11:04:26 -0700205 node = calloc(1, sizeof(struct node));
Brian Swetland03ee9472010-08-12 18:01:08 -0700206 if (node == 0) {
207 return 0;
208 }
Paul Eastham11ccdb32010-10-14 11:04:26 -0700209 node->name = malloc(namelen + 1);
210 if (node->name == 0) {
211 free(node);
212 return 0;
213 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700214
215 node->nid = nid;
216 node->gen = gen;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700217 add_node_to_parent(node, parent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700218 memcpy(node->name, name, namelen + 1);
219 node->namelen = namelen;
220 parent->refcount++;
221
222 return node;
223}
224
Paul Eastham11ccdb32010-10-14 11:04:26 -0700225static char *rename_node(struct node *node, const char *name)
226{
227 node->namelen = strlen(name);
228 char *newname = realloc(node->name, node->namelen + 1);
229 if (newname == 0)
230 return 0;
231 node->name = newname;
232 memcpy(node->name, name, node->namelen + 1);
233 return node->name;
234}
235
Brian Swetland03ee9472010-08-12 18:01:08 -0700236void fuse_init(struct fuse *fuse, int fd, const char *path)
237{
238 fuse->fd = fd;
239 fuse->next_node_id = 2;
240 fuse->next_generation = 0;
241
242 fuse->all = &fuse->root;
243
244 fuse->root.nid = FUSE_ROOT_ID; /* 1 */
245 fuse->root.next = 0;
246 fuse->root.child = 0;
247 fuse->root.parent = 0;
248
249 fuse->root.all = 0;
250 fuse->root.refcount = 2;
251
Paul Eastham11ccdb32010-10-14 11:04:26 -0700252 fuse->root.name = 0;
253 rename_node(&fuse->root, path);
Brian Swetland03ee9472010-08-12 18:01:08 -0700254}
255
256static inline void *id_to_ptr(__u64 nid)
257{
258 return (void *) nid;
259}
260
261static inline __u64 ptr_to_id(void *ptr)
262{
263 return (__u64) ptr;
264}
265
266
267struct node *lookup_by_inode(struct fuse *fuse, __u64 nid)
268{
269 if (nid == FUSE_ROOT_ID) {
270 return &fuse->root;
271 } else {
272 return id_to_ptr(nid);
273 }
274}
275
276struct node *lookup_child_by_name(struct node *node, const char *name)
277{
278 for (node = node->child; node; node = node->next) {
279 if (!strcmp(name, node->name)) {
280 return node;
281 }
282 }
283 return 0;
284}
285
286struct node *lookup_child_by_inode(struct node *node, __u64 nid)
287{
288 for (node = node->child; node; node = node->next) {
289 if (node->nid == nid) {
290 return node;
291 }
292 }
293 return 0;
294}
295
Paul Eastham11ccdb32010-10-14 11:04:26 -0700296static struct node *remove_child(struct node *parent, __u64 nid)
297{
298 struct node *prev = 0;
299 struct node *node;
300
301 for (node = parent->child; node; node = node->next) {
302 if (node->nid == nid) {
303 if (prev) {
304 prev->next = node->next;
305 } else {
306 parent->child = node->next;
307 }
308 node->next = 0;
309 node->parent = 0;
310 return node;
311 }
312 prev = node;
313 }
314 return 0;
315}
316
Brian Swetland03ee9472010-08-12 18:01:08 -0700317struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
318 struct fuse_attr *attr)
319{
320 int res;
321 struct stat s;
322 char *path, buffer[PATH_BUFFER_SIZE];
323 struct node *node;
324
325 path = node_get_path(parent, buffer, name);
326 /* XXX error? */
327
328 res = lstat(path, &s);
329 if (res < 0)
330 return 0;
331
332 node = lookup_child_by_name(parent, name);
333 if (!node) {
334 node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++);
335 if (!node)
336 return 0;
337 node->nid = ptr_to_id(node);
338 node->all = fuse->all;
339 fuse->all = node;
340 }
341
342 attr_from_stat(attr, &s);
343 attr->ino = node->nid;
344
345 return node;
346}
347
348void node_release(struct node *node)
349{
350 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
351 node->refcount--;
352 if (node->refcount == 0) {
353 if (node->parent->child == node) {
354 node->parent->child = node->parent->child->next;
355 } else {
356 struct node *node2;
357
358 node2 = node->parent->child;
359 while (node2->next != node)
360 node2 = node2->next;
361 node2->next = node->next;
362 }
363
364 TRACE("DESTROY %p (%s)\n", node, node->name);
365
366 node_release(node->parent);
367
368 node->parent = 0;
369 node->next = 0;
370
371 /* TODO: remove debugging - poison memory */
Paul Eastham11ccdb32010-10-14 11:04:26 -0700372 memset(node->name, 0xef, node->namelen);
373 free(node->name);
374 memset(node, 0xef, sizeof(*node));
Brian Swetland03ee9472010-08-12 18:01:08 -0700375 free(node);
376 }
377}
378
379void fuse_status(struct fuse *fuse, __u64 unique, int err)
380{
381 struct fuse_out_header hdr;
382 hdr.len = sizeof(hdr);
383 hdr.error = err;
384 hdr.unique = unique;
385 if (err) {
386// ERROR("*** %d ***\n", err);
387 }
388 write(fuse->fd, &hdr, sizeof(hdr));
389}
390
391void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
392{
393 struct fuse_out_header hdr;
394 struct iovec vec[2];
395 int res;
396
397 hdr.len = len + sizeof(hdr);
398 hdr.error = 0;
399 hdr.unique = unique;
400
401 vec[0].iov_base = &hdr;
402 vec[0].iov_len = sizeof(hdr);
403 vec[1].iov_base = data;
404 vec[1].iov_len = len;
405
406 res = writev(fuse->fd, vec, 2);
407 if (res < 0) {
408 ERROR("*** REPLY FAILED *** %d\n", errno);
409 }
410}
411
412void lookup_entry(struct fuse *fuse, struct node *node,
413 const char *name, __u64 unique)
414{
415 struct fuse_entry_out out;
416
417 memset(&out, 0, sizeof(out));
418
419 node = node_lookup(fuse, node, name, &out.attr);
420 if (!node) {
421 fuse_status(fuse, unique, -ENOENT);
422 return;
423 }
424
425 node->refcount++;
426// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
427 out.nodeid = node->nid;
428 out.generation = node->gen;
429 out.entry_valid = 10;
430 out.attr_valid = 10;
431
432 fuse_reply(fuse, unique, &out, sizeof(out));
433}
434
435void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
436{
437 struct node *node;
438
439 if ((len < sizeof(*hdr)) || (hdr->len != len)) {
440 ERROR("malformed header\n");
441 return;
442 }
443
444 len -= hdr->len;
445
446 if (hdr->nodeid) {
447 node = lookup_by_inode(fuse, hdr->nodeid);
448 if (!node) {
449 fuse_status(fuse, hdr->unique, -ENOENT);
450 return;
451 }
452 } else {
453 node = 0;
454 }
455
456 switch (hdr->opcode) {
457 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
458 TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
459 lookup_entry(fuse, node, (char*) data, hdr->unique);
460 return;
461 }
462 case FUSE_FORGET: {
463 struct fuse_forget_in *req = data;
464 TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
465 /* no reply */
466 while (req->nlookup--)
467 node_release(node);
468 return;
469 }
470 case FUSE_GETATTR: { /* getattr_in -> attr_out */
471 struct fuse_getattr_in *req = data;
472 struct fuse_attr_out out;
473
Paul Eastham11ccdb32010-10-14 11:04:26 -0700474 TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
Brian Swetland03ee9472010-08-12 18:01:08 -0700475
476 memset(&out, 0, sizeof(out));
477 node_get_attr(node, &out.attr);
478 out.attr_valid = 10;
479
480 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
481 return;
482 }
483 case FUSE_SETATTR: { /* setattr_in -> attr_out */
484 struct fuse_setattr_in *req = data;
485 struct fuse_attr_out out;
Paul Easthamf43219e2010-09-21 17:14:31 -0700486 char *path, buffer[PATH_BUFFER_SIZE];
487 int res = 0;
488
Brian Swetland03ee9472010-08-12 18:01:08 -0700489 TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
490 req->fh, hdr->nodeid, req->valid);
491
Paul Easthamf43219e2010-09-21 17:14:31 -0700492 /* XXX: incomplete implementation -- truncate only. chmod/chown
493 * should NEVER be implemented. */
494
495 path = node_get_path(node, buffer, 0);
496 if (req->valid & FATTR_SIZE)
497 res = truncate(path, req->size);
Brian Swetland03ee9472010-08-12 18:01:08 -0700498
499 memset(&out, 0, sizeof(out));
500 node_get_attr(node, &out.attr);
501 out.attr_valid = 10;
Paul Easthamf43219e2010-09-21 17:14:31 -0700502
503 if (res)
504 fuse_status(fuse, hdr->unique, -errno);
505 else
506 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Brian Swetland03ee9472010-08-12 18:01:08 -0700507 return;
508 }
509// case FUSE_READLINK:
510// case FUSE_SYMLINK:
511 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
512 struct fuse_mknod_in *req = data;
513 char *path, buffer[PATH_BUFFER_SIZE];
514 char *name = ((char*) data) + sizeof(*req);
515 int res;
516 TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
517 path = node_get_path(node, buffer, name);
518
519 req->mode = (req->mode & (~0777)) | 0664;
520 res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
521 if (res < 0) {
522 fuse_status(fuse, hdr->unique, -errno);
523 } else {
524 lookup_entry(fuse, node, name, hdr->unique);
525 }
526 return;
527 }
528 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
529 struct fuse_mkdir_in *req = data;
530 struct fuse_entry_out out;
531 char *path, buffer[PATH_BUFFER_SIZE];
532 char *name = ((char*) data) + sizeof(*req);
533 int res;
534 TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
535 path = node_get_path(node, buffer, name);
536
537 req->mode = (req->mode & (~0777)) | 0775;
538 res = mkdir(path, req->mode);
539 if (res < 0) {
540 fuse_status(fuse, hdr->unique, -errno);
541 } else {
542 lookup_entry(fuse, node, name, hdr->unique);
543 }
544 return;
545 }
546 case FUSE_UNLINK: { /* bytez[] -> */
547 char *path, buffer[PATH_BUFFER_SIZE];
548 int res;
549 TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
550 path = node_get_path(node, buffer, (char*) data);
551 res = unlink(path);
552 fuse_status(fuse, hdr->unique, res ? -errno : 0);
553 return;
554 }
555 case FUSE_RMDIR: { /* bytez[] -> */
556 char *path, buffer[PATH_BUFFER_SIZE];
557 int res;
558 TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
559 path = node_get_path(node, buffer, (char*) data);
560 res = rmdir(path);
561 fuse_status(fuse, hdr->unique, res ? -errno : 0);
562 return;
563 }
564 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
565 struct fuse_rename_in *req = data;
566 char *oldname = ((char*) data) + sizeof(*req);
567 char *newname = oldname + strlen(oldname) + 1;
568 char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
569 char *newpath, newbuffer[PATH_BUFFER_SIZE];
Paul Eastham11ccdb32010-10-14 11:04:26 -0700570 struct node *target;
571 struct node *newparent;
Brian Swetland03ee9472010-08-12 18:01:08 -0700572 int res;
573
Paul Eastham11ccdb32010-10-14 11:04:26 -0700574 TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
575
576 target = lookup_child_by_name(node, oldname);
577 if (!target) {
Brian Swetland03ee9472010-08-12 18:01:08 -0700578 fuse_status(fuse, hdr->unique, -ENOENT);
579 return;
580 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700581 oldpath = node_get_path(node, oldbuffer, oldname);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700582
583 newparent = lookup_by_inode(fuse, req->newdir);
584 if (!newparent) {
585 fuse_status(fuse, hdr->unique, -ENOENT);
586 return;
587 }
588 newpath = node_get_path(newparent, newbuffer, newname);
589
590 if (!remove_child(node, target->nid)) {
591 ERROR("RENAME remove_child not found");
592 fuse_status(fuse, hdr->unique, -ENOENT);
593 return;
594 }
595 if (!rename_node(target, newname)) {
596 fuse_status(fuse, hdr->unique, -ENOMEM);
597 return;
598 }
599 add_node_to_parent(target, newparent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700600
601 res = rename(oldpath, newpath);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700602 TRACE("RENAME result %d\n", res);
603
Brian Swetland03ee9472010-08-12 18:01:08 -0700604 fuse_status(fuse, hdr->unique, res ? -errno : 0);
605 return;
606 }
607// case FUSE_LINK:
608 case FUSE_OPEN: { /* open_in -> open_out */
609 struct fuse_open_in *req = data;
610 struct fuse_open_out out;
611 char *path, buffer[PATH_BUFFER_SIZE];
612 struct handle *h;
613
614 h = malloc(sizeof(*h));
615 if (!h) {
616 fuse_status(fuse, hdr->unique, -ENOMEM);
617 return;
618 }
619
620 path = node_get_path(node, buffer, 0);
621 TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
622 h->fd = open(path, req->flags);
623 if (h->fd < 0) {
624 ERROR("ERROR\n");
625 fuse_status(fuse, hdr->unique, errno);
626 free(h);
627 return;
628 }
629 out.fh = ptr_to_id(h);
630 out.open_flags = 0;
631 out.padding = 0;
632 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
633 return;
634 }
635 case FUSE_READ: { /* read_in -> byte[] */
636 char buffer[128 * 1024];
637 struct fuse_read_in *req = data;
638 struct handle *h = id_to_ptr(req->fh);
639 int res;
640 TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
641 if (req->size > sizeof(buffer)) {
642 fuse_status(fuse, hdr->unique, -EINVAL);
643 return;
644 }
645 res = pread(h->fd, buffer, req->size, req->offset);
646 if (res < 0) {
647 fuse_status(fuse, hdr->unique, errno);
648 return;
649 }
650 fuse_reply(fuse, hdr->unique, buffer, res);
651 return;
652 }
653 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
654 struct fuse_write_in *req = data;
655 struct fuse_write_out out;
656 struct handle *h = id_to_ptr(req->fh);
657 int res;
658 TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
659 res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
660 if (res < 0) {
661 fuse_status(fuse, hdr->unique, errno);
662 return;
663 }
664 out.size = res;
665 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
666 goto oops;
667 }
Mike Lockwood4553b082010-08-16 14:14:44 -0400668 case FUSE_STATFS: { /* getattr_in -> attr_out */
669 struct statfs stat;
670 struct fuse_statfs_out out;
671 int res;
672
673 TRACE("STATFS\n");
674
675 if (statfs(fuse->root.name, &stat)) {
676 fuse_status(fuse, hdr->unique, -errno);
677 return;
678 }
679
680 memset(&out, 0, sizeof(out));
681 out.st.blocks = stat.f_blocks;
682 out.st.bfree = stat.f_bfree;
683 out.st.bavail = stat.f_bavail;
684 out.st.files = stat.f_files;
685 out.st.ffree = stat.f_ffree;
686 out.st.bsize = stat.f_bsize;
687 out.st.namelen = stat.f_namelen;
688 out.st.frsize = stat.f_frsize;
689 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
690 return;
691 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700692 case FUSE_RELEASE: { /* release_in -> */
693 struct fuse_release_in *req = data;
694 struct handle *h = id_to_ptr(req->fh);
695 TRACE("RELEASE %p(%d)\n", h, h->fd);
696 close(h->fd);
697 free(h);
698 fuse_status(fuse, hdr->unique, 0);
699 return;
700 }
701// case FUSE_FSYNC:
702// case FUSE_SETXATTR:
703// case FUSE_GETXATTR:
704// case FUSE_LISTXATTR:
705// case FUSE_REMOVEXATTR:
706 case FUSE_FLUSH:
707 fuse_status(fuse, hdr->unique, 0);
708 return;
709 case FUSE_OPENDIR: { /* open_in -> open_out */
710 struct fuse_open_in *req = data;
711 struct fuse_open_out out;
712 char *path, buffer[PATH_BUFFER_SIZE];
713 struct dirhandle *h;
714
715 h = malloc(sizeof(*h));
716 if (!h) {
717 fuse_status(fuse, hdr->unique, -ENOMEM);
718 return;
719 }
720
721 path = node_get_path(node, buffer, 0);
722 TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
723 h->d = opendir(path);
724 if (h->d == 0) {
725 ERROR("ERROR\n");
726 fuse_status(fuse, hdr->unique, -errno);
727 free(h);
728 return;
729 }
730 out.fh = ptr_to_id(h);
731 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
732 return;
733 }
734 case FUSE_READDIR: {
735 struct fuse_read_in *req = data;
736 char buffer[8192];
737 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
738 struct dirent *de;
739 struct dirhandle *h = id_to_ptr(req->fh);
740 TRACE("READDIR %p\n", h);
741 de = readdir(h->d);
742 if (!de) {
743 fuse_status(fuse, hdr->unique, 0);
744 return;
745 }
746 fde->ino = FUSE_UNKNOWN_INO;
747 fde->off = 0;
748 fde->type = de->d_type;
749 fde->namelen = strlen(de->d_name);
750 memcpy(fde->name, de->d_name, fde->namelen + 1);
751 fuse_reply(fuse, hdr->unique, fde,
752 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
753 return;
754 }
755 case FUSE_RELEASEDIR: { /* release_in -> */
756 struct fuse_release_in *req = data;
757 struct dirhandle *h = id_to_ptr(req->fh);
758 TRACE("RELEASEDIR %p\n",h);
759 closedir(h->d);
760 free(h);
761 fuse_status(fuse, hdr->unique, 0);
762 return;
763 }
764// case FUSE_FSYNCDIR:
765 case FUSE_INIT: { /* init_in -> init_out */
766 struct fuse_init_in *req = data;
767 struct fuse_init_out out;
768
769 TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
770 req->major, req->minor, req->max_readahead, req->flags);
771
772 out.major = FUSE_KERNEL_VERSION;
773 out.minor = FUSE_KERNEL_MINOR_VERSION;
774 out.max_readahead = req->max_readahead;
Mike Lockwoodfc1a13b2010-08-20 10:29:29 -0400775 out.flags = FUSE_ATOMIC_O_TRUNC;
Brian Swetland03ee9472010-08-12 18:01:08 -0700776 out.max_background = 32;
777 out.congestion_threshold = 32;
778 out.max_write = 256 * 1024;
779
780 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
781 return;
782 }
783 default: {
784 struct fuse_out_header h;
785 ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
786 hdr->opcode, hdr->unique, hdr->nodeid);
787
788 oops:
789 h.len = sizeof(h);
790 h.error = -ENOSYS;
791 h.unique = hdr->unique;
792 write(fuse->fd, &h, sizeof(h));
793 break;
794 }
795 }
796}
797
798void handle_fuse_requests(struct fuse *fuse)
799{
800 unsigned char req[256 * 1024 + 128];
801 int len;
802
803 for (;;) {
804 len = read(fuse->fd, req, 8192);
805 if (len < 0) {
806 if (errno == EINTR)
807 continue;
808 ERROR("handle_fuse_requests: errno=%d\n", errno);
809 return;
810 }
811 handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
812 }
813}
814
815int main(int argc, char **argv)
816{
817 struct fuse fuse;
818 char opts[256];
819 int fd;
820 int res;
821 unsigned uid;
822 unsigned gid;
823 const char *path;
824
825 if (argc != 4) {
826 ERROR("usage: sdcard <path> <uid> <gid>\n");
827 return -1;
828 }
829
830 uid = strtoul(argv[2], 0, 10);
831 gid = strtoul(argv[3], 0, 10);
832 if (!uid || !gid) {
833 ERROR("uid and gid must be nonzero\n");
834 return -1;
835 }
836
837 path = argv[1];
838
839 /* cleanup from previous instance, if necessary */
Mike Lockwood4553b082010-08-16 14:14:44 -0400840 umount2(MOUNT_POINT, 2);
Brian Swetland03ee9472010-08-12 18:01:08 -0700841
842 fd = open("/dev/fuse", O_RDWR);
843 if (fd < 0){
844 ERROR("cannot open fuse device (%d)\n", errno);
845 return -1;
846 }
847
848 sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
849 "user_id=%d,group_id=%d", fd, uid, gid);
850
Mike Lockwood4553b082010-08-16 14:14:44 -0400851 res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
Brian Swetland03ee9472010-08-12 18:01:08 -0700852 if (res < 0) {
853 ERROR("cannot mount fuse filesystem (%d)\n", errno);
854 return -1;
855 }
856
857 if (setgid(gid) < 0) {
858 ERROR("cannot setgid!\n");
859 return -1;
860 }
861 if (setuid(uid) < 0) {
862 ERROR("cannot setuid!\n");
863 return -1;
864 }
865
866 fuse_init(&fuse, fd, path);
867
868 umask(0);
869 handle_fuse_requests(&fuse);
870
871 return 0;
872}