blob: 4f5a6b08276da7dc3485bddfa5c88e66e819bea5 [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
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500445static void normalize_name(char *name)
446{
447 char ch;
448 while ((ch = *name) != 0)
449 *name++ = tolower(ch);
450}
451
Brian Swetland03ee9472010-08-12 18:01:08 -0700452void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len)
453{
454 struct node *node;
455
456 if ((len < sizeof(*hdr)) || (hdr->len != len)) {
457 ERROR("malformed header\n");
458 return;
459 }
460
461 len -= hdr->len;
462
463 if (hdr->nodeid) {
464 node = lookup_by_inode(fuse, hdr->nodeid);
465 if (!node) {
466 fuse_status(fuse, hdr->unique, -ENOENT);
467 return;
468 }
469 } else {
470 node = 0;
471 }
472
473 switch (hdr->opcode) {
474 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500475 normalize_name((char*) data);
Brian Swetland03ee9472010-08-12 18:01:08 -0700476 TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data);
477 lookup_entry(fuse, node, (char*) data, hdr->unique);
478 return;
479 }
480 case FUSE_FORGET: {
481 struct fuse_forget_in *req = data;
482 TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup);
483 /* no reply */
484 while (req->nlookup--)
485 node_release(node);
486 return;
487 }
488 case FUSE_GETATTR: { /* getattr_in -> attr_out */
489 struct fuse_getattr_in *req = data;
490 struct fuse_attr_out out;
491
Paul Eastham11ccdb32010-10-14 11:04:26 -0700492 TRACE("GETATTR flags=%x fh=%llx\n", req->getattr_flags, req->fh);
Brian Swetland03ee9472010-08-12 18:01:08 -0700493
494 memset(&out, 0, sizeof(out));
495 node_get_attr(node, &out.attr);
496 out.attr_valid = 10;
497
498 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
499 return;
500 }
501 case FUSE_SETATTR: { /* setattr_in -> attr_out */
502 struct fuse_setattr_in *req = data;
503 struct fuse_attr_out out;
Paul Easthamf43219e2010-09-21 17:14:31 -0700504 char *path, buffer[PATH_BUFFER_SIZE];
505 int res = 0;
506
Brian Swetland03ee9472010-08-12 18:01:08 -0700507 TRACE("SETATTR fh=%llx id=%llx valid=%x\n",
508 req->fh, hdr->nodeid, req->valid);
509
Paul Easthamf43219e2010-09-21 17:14:31 -0700510 /* XXX: incomplete implementation -- truncate only. chmod/chown
511 * should NEVER be implemented. */
512
513 path = node_get_path(node, buffer, 0);
514 if (req->valid & FATTR_SIZE)
515 res = truncate(path, req->size);
Brian Swetland03ee9472010-08-12 18:01:08 -0700516
517 memset(&out, 0, sizeof(out));
518 node_get_attr(node, &out.attr);
519 out.attr_valid = 10;
Paul Easthamf43219e2010-09-21 17:14:31 -0700520
521 if (res)
522 fuse_status(fuse, hdr->unique, -errno);
523 else
524 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Brian Swetland03ee9472010-08-12 18:01:08 -0700525 return;
526 }
527// case FUSE_READLINK:
528// case FUSE_SYMLINK:
529 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
530 struct fuse_mknod_in *req = data;
531 char *path, buffer[PATH_BUFFER_SIZE];
532 char *name = ((char*) data) + sizeof(*req);
533 int res;
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500534
535 normalize_name(name);
536
Brian Swetland03ee9472010-08-12 18:01:08 -0700537 TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid);
538 path = node_get_path(node, buffer, name);
539
540 req->mode = (req->mode & (~0777)) | 0664;
541 res = mknod(path, req->mode, req->rdev); /* XXX perm?*/
542 if (res < 0) {
543 fuse_status(fuse, hdr->unique, -errno);
544 } else {
545 lookup_entry(fuse, node, name, hdr->unique);
546 }
547 return;
548 }
549 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
550 struct fuse_mkdir_in *req = data;
551 struct fuse_entry_out out;
552 char *path, buffer[PATH_BUFFER_SIZE];
553 char *name = ((char*) data) + sizeof(*req);
554 int res;
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500555
556 normalize_name(name);
557
Brian Swetland03ee9472010-08-12 18:01:08 -0700558 TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode);
559 path = node_get_path(node, buffer, name);
560
561 req->mode = (req->mode & (~0777)) | 0775;
562 res = mkdir(path, req->mode);
563 if (res < 0) {
564 fuse_status(fuse, hdr->unique, -errno);
565 } else {
566 lookup_entry(fuse, node, name, hdr->unique);
567 }
568 return;
569 }
570 case FUSE_UNLINK: { /* bytez[] -> */
571 char *path, buffer[PATH_BUFFER_SIZE];
572 int res;
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500573 normalize_name((char*) data);
Brian Swetland03ee9472010-08-12 18:01:08 -0700574 TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid);
575 path = node_get_path(node, buffer, (char*) data);
576 res = unlink(path);
577 fuse_status(fuse, hdr->unique, res ? -errno : 0);
578 return;
579 }
580 case FUSE_RMDIR: { /* bytez[] -> */
581 char *path, buffer[PATH_BUFFER_SIZE];
582 int res;
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500583 normalize_name((char*) data);
Brian Swetland03ee9472010-08-12 18:01:08 -0700584 TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid);
585 path = node_get_path(node, buffer, (char*) data);
586 res = rmdir(path);
587 fuse_status(fuse, hdr->unique, res ? -errno : 0);
588 return;
589 }
590 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
591 struct fuse_rename_in *req = data;
592 char *oldname = ((char*) data) + sizeof(*req);
593 char *newname = oldname + strlen(oldname) + 1;
594 char *oldpath, oldbuffer[PATH_BUFFER_SIZE];
595 char *newpath, newbuffer[PATH_BUFFER_SIZE];
Paul Eastham11ccdb32010-10-14 11:04:26 -0700596 struct node *target;
597 struct node *newparent;
Brian Swetland03ee9472010-08-12 18:01:08 -0700598 int res;
599
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500600 normalize_name(oldname);
601 normalize_name(newname);
602
Paul Eastham11ccdb32010-10-14 11:04:26 -0700603 TRACE("RENAME %s->%s @ %llx\n", oldname, newname, hdr->nodeid);
604
605 target = lookup_child_by_name(node, oldname);
606 if (!target) {
Brian Swetland03ee9472010-08-12 18:01:08 -0700607 fuse_status(fuse, hdr->unique, -ENOENT);
608 return;
609 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700610 oldpath = node_get_path(node, oldbuffer, oldname);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700611
612 newparent = lookup_by_inode(fuse, req->newdir);
613 if (!newparent) {
614 fuse_status(fuse, hdr->unique, -ENOENT);
615 return;
616 }
617 newpath = node_get_path(newparent, newbuffer, newname);
618
619 if (!remove_child(node, target->nid)) {
620 ERROR("RENAME remove_child not found");
621 fuse_status(fuse, hdr->unique, -ENOENT);
622 return;
623 }
624 if (!rename_node(target, newname)) {
625 fuse_status(fuse, hdr->unique, -ENOMEM);
626 return;
627 }
628 add_node_to_parent(target, newparent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700629
630 res = rename(oldpath, newpath);
Paul Eastham11ccdb32010-10-14 11:04:26 -0700631 TRACE("RENAME result %d\n", res);
632
Brian Swetland03ee9472010-08-12 18:01:08 -0700633 fuse_status(fuse, hdr->unique, res ? -errno : 0);
634 return;
635 }
636// case FUSE_LINK:
637 case FUSE_OPEN: { /* open_in -> open_out */
638 struct fuse_open_in *req = data;
639 struct fuse_open_out out;
640 char *path, buffer[PATH_BUFFER_SIZE];
641 struct handle *h;
642
643 h = malloc(sizeof(*h));
644 if (!h) {
645 fuse_status(fuse, hdr->unique, -ENOMEM);
646 return;
647 }
648
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500649 normalize_name(buffer);
Brian Swetland03ee9472010-08-12 18:01:08 -0700650 path = node_get_path(node, buffer, 0);
651 TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h);
652 h->fd = open(path, req->flags);
653 if (h->fd < 0) {
654 ERROR("ERROR\n");
655 fuse_status(fuse, hdr->unique, errno);
656 free(h);
657 return;
658 }
659 out.fh = ptr_to_id(h);
660 out.open_flags = 0;
661 out.padding = 0;
662 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
663 return;
664 }
665 case FUSE_READ: { /* read_in -> byte[] */
666 char buffer[128 * 1024];
667 struct fuse_read_in *req = data;
668 struct handle *h = id_to_ptr(req->fh);
669 int res;
670 TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
671 if (req->size > sizeof(buffer)) {
672 fuse_status(fuse, hdr->unique, -EINVAL);
673 return;
674 }
Kenny Root90749772011-01-11 15:38:31 -0800675 res = pread64(h->fd, buffer, req->size, req->offset);
Brian Swetland03ee9472010-08-12 18:01:08 -0700676 if (res < 0) {
677 fuse_status(fuse, hdr->unique, errno);
678 return;
679 }
680 fuse_reply(fuse, hdr->unique, buffer, res);
681 return;
682 }
683 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
684 struct fuse_write_in *req = data;
685 struct fuse_write_out out;
686 struct handle *h = id_to_ptr(req->fh);
687 int res;
688 TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset);
Kenny Root90749772011-01-11 15:38:31 -0800689 res = pwrite64(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset);
Brian Swetland03ee9472010-08-12 18:01:08 -0700690 if (res < 0) {
691 fuse_status(fuse, hdr->unique, errno);
692 return;
693 }
694 out.size = res;
695 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
696 goto oops;
697 }
Mike Lockwood4553b082010-08-16 14:14:44 -0400698 case FUSE_STATFS: { /* getattr_in -> attr_out */
699 struct statfs stat;
700 struct fuse_statfs_out out;
701 int res;
702
703 TRACE("STATFS\n");
704
705 if (statfs(fuse->root.name, &stat)) {
706 fuse_status(fuse, hdr->unique, -errno);
707 return;
708 }
709
710 memset(&out, 0, sizeof(out));
711 out.st.blocks = stat.f_blocks;
712 out.st.bfree = stat.f_bfree;
713 out.st.bavail = stat.f_bavail;
714 out.st.files = stat.f_files;
715 out.st.ffree = stat.f_ffree;
716 out.st.bsize = stat.f_bsize;
717 out.st.namelen = stat.f_namelen;
718 out.st.frsize = stat.f_frsize;
719 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
720 return;
721 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700722 case FUSE_RELEASE: { /* release_in -> */
723 struct fuse_release_in *req = data;
724 struct handle *h = id_to_ptr(req->fh);
725 TRACE("RELEASE %p(%d)\n", h, h->fd);
726 close(h->fd);
727 free(h);
728 fuse_status(fuse, hdr->unique, 0);
729 return;
730 }
731// case FUSE_FSYNC:
732// case FUSE_SETXATTR:
733// case FUSE_GETXATTR:
734// case FUSE_LISTXATTR:
735// case FUSE_REMOVEXATTR:
736 case FUSE_FLUSH:
737 fuse_status(fuse, hdr->unique, 0);
738 return;
739 case FUSE_OPENDIR: { /* open_in -> open_out */
740 struct fuse_open_in *req = data;
741 struct fuse_open_out out;
742 char *path, buffer[PATH_BUFFER_SIZE];
743 struct dirhandle *h;
744
745 h = malloc(sizeof(*h));
746 if (!h) {
747 fuse_status(fuse, hdr->unique, -ENOMEM);
748 return;
749 }
750
Mike Lockwood51b3a2d2011-01-12 07:35:46 -0500751 normalize_name(buffer);
Brian Swetland03ee9472010-08-12 18:01:08 -0700752 path = node_get_path(node, buffer, 0);
753 TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path);
754 h->d = opendir(path);
755 if (h->d == 0) {
756 ERROR("ERROR\n");
757 fuse_status(fuse, hdr->unique, -errno);
758 free(h);
759 return;
760 }
761 out.fh = ptr_to_id(h);
762 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
763 return;
764 }
765 case FUSE_READDIR: {
766 struct fuse_read_in *req = data;
767 char buffer[8192];
768 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
769 struct dirent *de;
770 struct dirhandle *h = id_to_ptr(req->fh);
771 TRACE("READDIR %p\n", h);
772 de = readdir(h->d);
773 if (!de) {
774 fuse_status(fuse, hdr->unique, 0);
775 return;
776 }
777 fde->ino = FUSE_UNKNOWN_INO;
778 fde->off = 0;
779 fde->type = de->d_type;
780 fde->namelen = strlen(de->d_name);
781 memcpy(fde->name, de->d_name, fde->namelen + 1);
782 fuse_reply(fuse, hdr->unique, fde,
783 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
784 return;
785 }
786 case FUSE_RELEASEDIR: { /* release_in -> */
787 struct fuse_release_in *req = data;
788 struct dirhandle *h = id_to_ptr(req->fh);
789 TRACE("RELEASEDIR %p\n",h);
790 closedir(h->d);
791 free(h);
792 fuse_status(fuse, hdr->unique, 0);
793 return;
794 }
795// case FUSE_FSYNCDIR:
796 case FUSE_INIT: { /* init_in -> init_out */
797 struct fuse_init_in *req = data;
798 struct fuse_init_out out;
799
800 TRACE("INIT ver=%d.%d maxread=%d flags=%x\n",
801 req->major, req->minor, req->max_readahead, req->flags);
802
803 out.major = FUSE_KERNEL_VERSION;
804 out.minor = FUSE_KERNEL_MINOR_VERSION;
805 out.max_readahead = req->max_readahead;
Mike Lockwoodfc1a13b2010-08-20 10:29:29 -0400806 out.flags = FUSE_ATOMIC_O_TRUNC;
Brian Swetland03ee9472010-08-12 18:01:08 -0700807 out.max_background = 32;
808 out.congestion_threshold = 32;
809 out.max_write = 256 * 1024;
810
811 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
812 return;
813 }
814 default: {
815 struct fuse_out_header h;
816 ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n",
817 hdr->opcode, hdr->unique, hdr->nodeid);
818
819 oops:
820 h.len = sizeof(h);
821 h.error = -ENOSYS;
822 h.unique = hdr->unique;
823 write(fuse->fd, &h, sizeof(h));
824 break;
825 }
826 }
827}
828
829void handle_fuse_requests(struct fuse *fuse)
830{
831 unsigned char req[256 * 1024 + 128];
832 int len;
833
834 for (;;) {
835 len = read(fuse->fd, req, 8192);
836 if (len < 0) {
837 if (errno == EINTR)
838 continue;
839 ERROR("handle_fuse_requests: errno=%d\n", errno);
840 return;
841 }
842 handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len);
843 }
844}
845
846int main(int argc, char **argv)
847{
848 struct fuse fuse;
849 char opts[256];
850 int fd;
851 int res;
852 unsigned uid;
853 unsigned gid;
854 const char *path;
855
856 if (argc != 4) {
857 ERROR("usage: sdcard <path> <uid> <gid>\n");
858 return -1;
859 }
860
861 uid = strtoul(argv[2], 0, 10);
862 gid = strtoul(argv[3], 0, 10);
863 if (!uid || !gid) {
864 ERROR("uid and gid must be nonzero\n");
865 return -1;
866 }
867
868 path = argv[1];
869
870 /* cleanup from previous instance, if necessary */
Mike Lockwood4553b082010-08-16 14:14:44 -0400871 umount2(MOUNT_POINT, 2);
Brian Swetland03ee9472010-08-12 18:01:08 -0700872
873 fd = open("/dev/fuse", O_RDWR);
874 if (fd < 0){
875 ERROR("cannot open fuse device (%d)\n", errno);
876 return -1;
877 }
878
879 sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other,"
880 "user_id=%d,group_id=%d", fd, uid, gid);
881
Mike Lockwood4553b082010-08-16 14:14:44 -0400882 res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
Brian Swetland03ee9472010-08-12 18:01:08 -0700883 if (res < 0) {
884 ERROR("cannot mount fuse filesystem (%d)\n", errno);
885 return -1;
886 }
887
888 if (setgid(gid) < 0) {
889 ERROR("cannot setgid!\n");
890 return -1;
891 }
892 if (setuid(uid) < 0) {
893 ERROR("cannot setuid!\n");
894 return -1;
895 }
896
897 fuse_init(&fuse, fd, path);
898
899 umask(0);
900 handle_fuse_requests(&fuse);
901
902 return 0;
903}