blob: 9dda0ea37d85791cdd4d9ea5722be11085dbea15 [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;
Paul Eastham77085c52011-01-04 21:06:03 -0800198 parent->refcount++;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700199}
200
Brian Swetland03ee9472010-08-12 18:01:08 -0700201struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen)
202{
203 struct node *node;
204 int namelen = strlen(name);
205
Paul Eastham11ccdb32010-10-14 11:04:26 -0700206 node = calloc(1, sizeof(struct node));
Brian Swetland03ee9472010-08-12 18:01:08 -0700207 if (node == 0) {
208 return 0;
209 }
Paul Eastham11ccdb32010-10-14 11:04:26 -0700210 node->name = malloc(namelen + 1);
211 if (node->name == 0) {
212 free(node);
213 return 0;
214 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700215
216 node->nid = nid;
217 node->gen = gen;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700218 add_node_to_parent(node, parent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700219 memcpy(node->name, name, namelen + 1);
220 node->namelen = namelen;
Brian Swetland03ee9472010-08-12 18:01:08 -0700221
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 Eastham77085c52011-01-04 21:06:03 -0800296static void dec_refcount(struct node *node) {
297 if (node->refcount > 0) {
298 node->refcount--;
299 TRACE("dec_refcount %p(%s) -> %d\n", node, node->name, node->refcount);
300 } else {
301 ERROR("Zero refcnt %p\n", node);
302 }
303 }
304
Paul Eastham11ccdb32010-10-14 11:04:26 -0700305static struct node *remove_child(struct node *parent, __u64 nid)
306{
307 struct node *prev = 0;
308 struct node *node;
309
310 for (node = parent->child; node; node = node->next) {
311 if (node->nid == nid) {
312 if (prev) {
313 prev->next = node->next;
314 } else {
315 parent->child = node->next;
316 }
317 node->next = 0;
318 node->parent = 0;
Paul Eastham77085c52011-01-04 21:06:03 -0800319 dec_refcount(parent);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700320 return node;
321 }
322 prev = node;
323 }
324 return 0;
325}
326
Brian Swetland03ee9472010-08-12 18:01:08 -0700327struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name,
328 struct fuse_attr *attr)
329{
330 int res;
331 struct stat s;
332 char *path, buffer[PATH_BUFFER_SIZE];
333 struct node *node;
334
335 path = node_get_path(parent, buffer, name);
336 /* XXX error? */
337
338 res = lstat(path, &s);
339 if (res < 0)
340 return 0;
341
342 node = lookup_child_by_name(parent, name);
343 if (!node) {
344 node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++);
345 if (!node)
346 return 0;
347 node->nid = ptr_to_id(node);
348 node->all = fuse->all;
349 fuse->all = node;
350 }
351
352 attr_from_stat(attr, &s);
353 attr->ino = node->nid;
354
355 return node;
356}
357
358void node_release(struct node *node)
359{
360 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
Paul Eastham77085c52011-01-04 21:06:03 -0800361 dec_refcount(node);
Brian Swetland03ee9472010-08-12 18:01:08 -0700362 if (node->refcount == 0) {
363 if (node->parent->child == node) {
364 node->parent->child = node->parent->child->next;
365 } else {
366 struct node *node2;
367
368 node2 = node->parent->child;
369 while (node2->next != node)
370 node2 = node2->next;
371 node2->next = node->next;
372 }
373
374 TRACE("DESTROY %p (%s)\n", node, node->name);
375
376 node_release(node->parent);
377
378 node->parent = 0;
379 node->next = 0;
380
381 /* TODO: remove debugging - poison memory */
Paul Eastham11ccdb32010-10-14 11:04:26 -0700382 memset(node->name, 0xef, node->namelen);
383 free(node->name);
Paul Eastham77085c52011-01-04 21:06:03 -0800384 memset(node, 0xfc, sizeof(*node));
Brian Swetland03ee9472010-08-12 18:01:08 -0700385 free(node);
386 }
387}
388
389void fuse_status(struct fuse *fuse, __u64 unique, int err)
390{
391 struct fuse_out_header hdr;
392 hdr.len = sizeof(hdr);
393 hdr.error = err;
394 hdr.unique = unique;
395 if (err) {
396// ERROR("*** %d ***\n", err);
397 }
398 write(fuse->fd, &hdr, sizeof(hdr));
399}
400
401void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
402{
403 struct fuse_out_header hdr;
404 struct iovec vec[2];
405 int res;
406
407 hdr.len = len + sizeof(hdr);
408 hdr.error = 0;
409 hdr.unique = unique;
410
411 vec[0].iov_base = &hdr;
412 vec[0].iov_len = sizeof(hdr);
413 vec[1].iov_base = data;
414 vec[1].iov_len = len;
415
416 res = writev(fuse->fd, vec, 2);
417 if (res < 0) {
418 ERROR("*** REPLY FAILED *** %d\n", errno);
419 }
420}
421
422void lookup_entry(struct fuse *fuse, struct node *node,
423 const char *name, __u64 unique)
424{
425 struct fuse_entry_out out;
426
427 memset(&out, 0, sizeof(out));
428
429 node = node_lookup(fuse, node, name, &out.attr);
430 if (!node) {
431 fuse_status(fuse, unique, -ENOENT);
432 return;
433 }
434
435 node->refcount++;
436// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
437 out.nodeid = node->nid;
438 out.generation = node->gen;
439 out.entry_valid = 10;
440 out.attr_valid = 10;
441
442 fuse_reply(fuse, unique, &out, sizeof(out));
443}
444
445void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
446{
447 struct node *node;
448
449 if ((len < sizeof(*hdr)) || (hdr->len != len)) {
450 ERROR("malformed header\n");
451 return;
452 }
453
454 len -= hdr->len;
455
456 if (hdr->nodeid) {
457 node = lookup_by_inode(fuse, hdr->nodeid);
458 if (!node) {
459 fuse_status(fuse, hdr->unique, -ENOENT);
460 return;
461 }
462 } else {
463 node = 0;
464 }
465
466 switch (hdr->opcode) {
467 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
468 TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
469 lookup_entry(fuse, node, (char*) data, hdr->unique);
470 return;
471 }
472 case FUSE_FORGET: {
473 struct fuse_forget_in *req = data;
474 TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
475 /* no reply */
476 while (req->nlookup--)
477 node_release(node);
478 return;
479 }
480 case FUSE_GETATTR: { /* getattr_in -> attr_out */
481 struct fuse_getattr_in *req = data;
482 struct fuse_attr_out out;
483
Paul Eastham11ccdb32010-10-14 11:04:26 -0700484 TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
Brian Swetland03ee9472010-08-12 18:01:08 -0700485
486 memset(&out, 0, sizeof(out));
487 node_get_attr(node, &out.attr);
488 out.attr_valid = 10;
489
490 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
491 return;
492 }
493 case FUSE_SETATTR: { /* setattr_in -> attr_out */
494 struct fuse_setattr_in *req = data;
495 struct fuse_attr_out out;
Paul Easthamf43219e2010-09-21 17:14:31 -0700496 char *path, buffer[PATH_BUFFER_SIZE];
497 int res = 0;
498
Brian Swetland03ee9472010-08-12 18:01:08 -0700499 TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
500 req->fh, hdr->nodeid, req->valid);
501
Paul Easthamf43219e2010-09-21 17:14:31 -0700502 /* XXX: incomplete implementation -- truncate only. chmod/chown
503 * should NEVER be implemented. */
504
505 path = node_get_path(node, buffer, 0);
506 if (req->valid & FATTR_SIZE)
507 res = truncate(path, req->size);
Brian Swetland03ee9472010-08-12 18:01:08 -0700508
509 memset(&out, 0, sizeof(out));
510 node_get_attr(node, &out.attr);
511 out.attr_valid = 10;
Paul Easthamf43219e2010-09-21 17:14:31 -0700512
513 if (res)
514 fuse_status(fuse, hdr->unique, -errno);
515 else
516 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Brian Swetland03ee9472010-08-12 18:01:08 -0700517 return;
518 }
519// case FUSE_READLINK:
520// case FUSE_SYMLINK:
521 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
522 struct fuse_mknod_in *req = data;
523 char *path, buffer[PATH_BUFFER_SIZE];
524 char *name = ((char*) data) + sizeof(*req);
525 int res;
526 TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
527 path = node_get_path(node, buffer, name);
528
529 req->mode = (req->mode & (~0777)) | 0664;
530 res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
531 if (res < 0) {
532 fuse_status(fuse, hdr->unique, -errno);
533 } else {
534 lookup_entry(fuse, node, name, hdr->unique);
535 }
536 return;
537 }
538 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
539 struct fuse_mkdir_in *req = data;
540 struct fuse_entry_out out;
541 char *path, buffer[PATH_BUFFER_SIZE];
542 char *name = ((char*) data) + sizeof(*req);
543 int res;
544 TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
545 path = node_get_path(node, buffer, name);
546
547 req->mode = (req->mode & (~0777)) | 0775;
548 res = mkdir(path, req->mode);
549 if (res < 0) {
550 fuse_status(fuse, hdr->unique, -errno);
551 } else {
552 lookup_entry(fuse, node, name, hdr->unique);
553 }
554 return;
555 }
556 case FUSE_UNLINK: { /* bytez[] -> */
557 char *path, buffer[PATH_BUFFER_SIZE];
558 int res;
559 TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
560 path = node_get_path(node, buffer, (char*) data);
561 res = unlink(path);
562 fuse_status(fuse, hdr->unique, res ? -errno : 0);
563 return;
564 }
565 case FUSE_RMDIR: { /* bytez[] -> */
566 char *path, buffer[PATH_BUFFER_SIZE];
567 int res;
568 TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
569 path = node_get_path(node, buffer, (char*) data);
570 res = rmdir(path);
571 fuse_status(fuse, hdr->unique, res ? -errno : 0);
572 return;
573 }
574 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
575 struct fuse_rename_in *req = data;
576 char *oldname = ((char*) data) + sizeof(*req);
577 char *newname = oldname + strlen(oldname) + 1;
578 char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
579 char *newpath, newbuffer[PATH_BUFFER_SIZE];
Paul Eastham11ccdb32010-10-14 11:04:26 -0700580 struct node *target;
581 struct node *newparent;
Brian Swetland03ee9472010-08-12 18:01:08 -0700582 int res;
583
Paul Eastham11ccdb32010-10-14 11:04:26 -0700584 TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
585
586 target = lookup_child_by_name(node, oldname);
587 if (!target) {
Brian Swetland03ee9472010-08-12 18:01:08 -0700588 fuse_status(fuse, hdr->unique, -ENOENT);
589 return;
590 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700591 oldpath = node_get_path(node, oldbuffer, oldname);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700592
593 newparent = lookup_by_inode(fuse, req->newdir);
594 if (!newparent) {
595 fuse_status(fuse, hdr->unique, -ENOENT);
596 return;
597 }
598 newpath = node_get_path(newparent, newbuffer, newname);
599
600 if (!remove_child(node, target->nid)) {
601 ERROR("RENAME remove_child not found");
602 fuse_status(fuse, hdr->unique, -ENOENT);
603 return;
604 }
605 if (!rename_node(target, newname)) {
606 fuse_status(fuse, hdr->unique, -ENOMEM);
607 return;
608 }
609 add_node_to_parent(target, newparent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700610
611 res = rename(oldpath, newpath);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700612 TRACE("RENAME result %d\n", res);
613
Brian Swetland03ee9472010-08-12 18:01:08 -0700614 fuse_status(fuse, hdr->unique, res ? -errno : 0);
615 return;
616 }
617// case FUSE_LINK:
618 case FUSE_OPEN: { /* open_in -> open_out */
619 struct fuse_open_in *req = data;
620 struct fuse_open_out out;
621 char *path, buffer[PATH_BUFFER_SIZE];
622 struct handle *h;
623
624 h = malloc(sizeof(*h));
625 if (!h) {
626 fuse_status(fuse, hdr->unique, -ENOMEM);
627 return;
628 }
629
630 path = node_get_path(node, buffer, 0);
631 TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
632 h->fd = open(path, req->flags);
633 if (h->fd < 0) {
634 ERROR("ERROR\n");
635 fuse_status(fuse, hdr->unique, errno);
636 free(h);
637 return;
638 }
639 out.fh = ptr_to_id(h);
640 out.open_flags = 0;
641 out.padding = 0;
642 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
643 return;
644 }
645 case FUSE_READ: { /* read_in -> byte[] */
646 char buffer[128 * 1024];
647 struct fuse_read_in *req = data;
648 struct handle *h = id_to_ptr(req->fh);
649 int res;
650 TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
651 if (req->size > sizeof(buffer)) {
652 fuse_status(fuse, hdr->unique, -EINVAL);
653 return;
654 }
655 res = pread(h->fd, buffer, req->size, req->offset);
656 if (res < 0) {
657 fuse_status(fuse, hdr->unique, errno);
658 return;
659 }
660 fuse_reply(fuse, hdr->unique, buffer, res);
661 return;
662 }
663 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
664 struct fuse_write_in *req = data;
665 struct fuse_write_out out;
666 struct handle *h = id_to_ptr(req->fh);
667 int res;
668 TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
669 res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
670 if (res < 0) {
671 fuse_status(fuse, hdr->unique, errno);
672 return;
673 }
674 out.size = res;
675 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
676 goto oops;
677 }
Mike Lockwood4553b082010-08-16 14:14:44 -0400678 case FUSE_STATFS: { /* getattr_in -> attr_out */
679 struct statfs stat;
680 struct fuse_statfs_out out;
681 int res;
682
683 TRACE("STATFS\n");
684
685 if (statfs(fuse->root.name, &stat)) {
686 fuse_status(fuse, hdr->unique, -errno);
687 return;
688 }
689
690 memset(&out, 0, sizeof(out));
691 out.st.blocks = stat.f_blocks;
692 out.st.bfree = stat.f_bfree;
693 out.st.bavail = stat.f_bavail;
694 out.st.files = stat.f_files;
695 out.st.ffree = stat.f_ffree;
696 out.st.bsize = stat.f_bsize;
697 out.st.namelen = stat.f_namelen;
698 out.st.frsize = stat.f_frsize;
699 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
700 return;
701 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700702 case FUSE_RELEASE: { /* release_in -> */
703 struct fuse_release_in *req = data;
704 struct handle *h = id_to_ptr(req->fh);
705 TRACE("RELEASE %p(%d)\n", h, h->fd);
706 close(h->fd);
707 free(h);
708 fuse_status(fuse, hdr->unique, 0);
709 return;
710 }
711// case FUSE_FSYNC:
712// case FUSE_SETXATTR:
713// case FUSE_GETXATTR:
714// case FUSE_LISTXATTR:
715// case FUSE_REMOVEXATTR:
716 case FUSE_FLUSH:
717 fuse_status(fuse, hdr->unique, 0);
718 return;
719 case FUSE_OPENDIR: { /* open_in -> open_out */
720 struct fuse_open_in *req = data;
721 struct fuse_open_out out;
722 char *path, buffer[PATH_BUFFER_SIZE];
723 struct dirhandle *h;
724
725 h = malloc(sizeof(*h));
726 if (!h) {
727 fuse_status(fuse, hdr->unique, -ENOMEM);
728 return;
729 }
730
731 path = node_get_path(node, buffer, 0);
732 TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
733 h->d = opendir(path);
734 if (h->d == 0) {
735 ERROR("ERROR\n");
736 fuse_status(fuse, hdr->unique, -errno);
737 free(h);
738 return;
739 }
740 out.fh = ptr_to_id(h);
741 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
742 return;
743 }
744 case FUSE_READDIR: {
745 struct fuse_read_in *req = data;
746 char buffer[8192];
747 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
748 struct dirent *de;
749 struct dirhandle *h = id_to_ptr(req->fh);
750 TRACE("READDIR %p\n", h);
751 de = readdir(h->d);
752 if (!de) {
753 fuse_status(fuse, hdr->unique, 0);
754 return;
755 }
756 fde->ino = FUSE_UNKNOWN_INO;
757 fde->off = 0;
758 fde->type = de->d_type;
759 fde->namelen = strlen(de->d_name);
760 memcpy(fde->name, de->d_name, fde->namelen + 1);
761 fuse_reply(fuse, hdr->unique, fde,
762 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
763 return;
764 }
765 case FUSE_RELEASEDIR: { /* release_in -> */
766 struct fuse_release_in *req = data;
767 struct dirhandle *h = id_to_ptr(req->fh);
768 TRACE("RELEASEDIR %p\n",h);
769 closedir(h->d);
770 free(h);
771 fuse_status(fuse, hdr->unique, 0);
772 return;
773 }
774// case FUSE_FSYNCDIR:
775 case FUSE_INIT: { /* init_in -> init_out */
776 struct fuse_init_in *req = data;
777 struct fuse_init_out out;
778
779 TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
780 req->major, req->minor, req->max_readahead, req->flags);
781
782 out.major = FUSE_KERNEL_VERSION;
783 out.minor = FUSE_KERNEL_MINOR_VERSION;
784 out.max_readahead = req->max_readahead;
Mike Lockwoodfc1a13b2010-08-20 10:29:29 -0400785 out.flags = FUSE_ATOMIC_O_TRUNC;
Brian Swetland03ee9472010-08-12 18:01:08 -0700786 out.max_background = 32;
787 out.congestion_threshold = 32;
788 out.max_write = 256 * 1024;
789
790 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
791 return;
792 }
793 default: {
794 struct fuse_out_header h;
795 ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
796 hdr->opcode, hdr->unique, hdr->nodeid);
797
798 oops:
799 h.len = sizeof(h);
800 h.error = -ENOSYS;
801 h.unique = hdr->unique;
802 write(fuse->fd, &h, sizeof(h));
803 break;
804 }
805 }
806}
807
808void handle_fuse_requests(struct fuse *fuse)
809{
810 unsigned char req[256 * 1024 + 128];
811 int len;
812
813 for (;;) {
814 len = read(fuse->fd, req, 8192);
815 if (len < 0) {
816 if (errno == EINTR)
817 continue;
818 ERROR("handle_fuse_requests: errno=%d\n", errno);
819 return;
820 }
821 handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
822 }
823}
824
825int main(int argc, char **argv)
826{
827 struct fuse fuse;
828 char opts[256];
829 int fd;
830 int res;
831 unsigned uid;
832 unsigned gid;
833 const char *path;
834
835 if (argc != 4) {
836 ERROR("usage: sdcard <path> <uid> <gid>\n");
837 return -1;
838 }
839
840 uid = strtoul(argv[2], 0, 10);
841 gid = strtoul(argv[3], 0, 10);
842 if (!uid || !gid) {
843 ERROR("uid and gid must be nonzero\n");
844 return -1;
845 }
846
847 path = argv[1];
848
849 /* cleanup from previous instance, if necessary */
Mike Lockwood4553b082010-08-16 14:14:44 -0400850 umount2(MOUNT_POINT, 2);
Brian Swetland03ee9472010-08-12 18:01:08 -0700851
852 fd = open("/dev/fuse", O_RDWR);
853 if (fd < 0){
854 ERROR("cannot open fuse device (%d)\n", errno);
855 return -1;
856 }
857
858 sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
859 "user_id=%d,group_id=%d", fd, uid, gid);
860
Mike Lockwood4553b082010-08-16 14:14:44 -0400861 res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
Brian Swetland03ee9472010-08-12 18:01:08 -0700862 if (res < 0) {
863 ERROR("cannot mount fuse filesystem (%d)\n", errno);
864 return -1;
865 }
866
867 if (setgid(gid) < 0) {
868 ERROR("cannot setgid!\n");
869 return -1;
870 }
871 if (setuid(uid) < 0) {
872 ERROR("cannot setuid!\n");
873 return -1;
874 }
875
876 fuse_init(&fuse, fd, path);
877
878 umask(0);
879 handle_fuse_requests(&fuse);
880
881 return 0;
882}