sdcard.c revision f43219e0b1022b257499289ceb951f6a1a44bf9c
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#include <stdio.h> 18#include <stdlib.h> 19#include <string.h> 20#include <unistd.h> 21#include <errno.h> 22#include <fcntl.h> 23#include <sys/mount.h> 24#include <sys/stat.h> 25#include <sys/statfs.h> 26#include <sys/uio.h> 27#include <dirent.h> 28 29#include <private/android_filesystem_config.h> 30 31#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 45 * mounts a filesystem on /mnt/sdcard. It will refuse to run if uid or 46 * 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 * 58 * 59 * Bugs: 60 * 61 * - need to move/rename node on RENAME 62 */ 63 64#define FUSE_TRACE 0 65 66#if FUSE_TRACE 67#define TRACE(x...) fprintf(stderr,x) 68#else 69#define TRACE(x...) do {} while (0) 70#endif 71 72#define ERROR(x...) fprintf(stderr,x) 73 74#define FUSE_UNKNOWN_INO 0xffffffff 75 76#define MOUNT_POINT "/mnt/sdcard" 77 78struct handle { 79 struct node *node; 80 int fd; 81}; 82 83struct dirhandle { 84 struct node *node; 85 DIR *d; 86}; 87 88struct node { 89 __u64 nid; 90 __u64 gen; 91 92 struct node *next; 93 struct node *child; 94 struct node *all; 95 struct node *parent; 96 97 __u32 refcount; 98 __u32 namelen; 99 100 char name[1]; 101}; 102 103struct fuse { 104 __u64 next_generation; 105 __u64 next_node_id; 106 107 int fd; 108 109 struct node *all; 110 111 struct node root; 112 char rootpath[1024]; 113}; 114 115#define PATH_BUFFER_SIZE 1024 116 117/* 118 * Get the real-life absolute path to a node. 119 * node: start at this node 120 * buf: storage for returned string 121 * name: append this string to path if set 122 */ 123char *node_get_path(struct node *node, char *buf, const char *name) 124{ 125 char *out = buf + PATH_BUFFER_SIZE - 1; 126 int len; 127 out[0] = 0; 128 129 if (name) { 130 len = strlen(name); 131 goto start; 132 } 133 134 while (node) { 135 name = node->name; 136 len = node->namelen; 137 node = node->parent; 138 start: 139 if ((len + 1) > (out - buf)) 140 return 0; 141 out -= len; 142 memcpy(out, name, len); 143 out --; 144 out[0] = '/'; 145 } 146 147 return out; 148} 149 150void attr_from_stat(struct fuse_attr *attr, struct stat *s) 151{ 152 attr->ino = s->st_ino; 153 attr->size = s->st_size; 154 attr->blocks = s->st_blocks; 155 attr->atime = s->st_atime; 156 attr->mtime = s->st_mtime; 157 attr->ctime = s->st_ctime; 158 attr->atimensec = s->st_atime_nsec; 159 attr->mtimensec = s->st_mtime_nsec; 160 attr->ctimensec = s->st_ctime_nsec; 161 attr->mode = s->st_mode; 162 attr->nlink = s->st_nlink; 163 164 /* force permissions to something reasonable: 165 * world readable 166 * writable by the sdcard group 167 */ 168 if (attr->mode & 0100) { 169 attr->mode = (attr->mode & (~0777)) | 0775; 170 } else { 171 attr->mode = (attr->mode & (~0777)) | 0664; 172 } 173 174 /* all files owned by root.sdcard */ 175 attr->uid = 0; 176 attr->gid = AID_SDCARD_RW; 177} 178 179int node_get_attr(struct node *node, struct fuse_attr *attr) 180{ 181 int res; 182 struct stat s; 183 char *path, buffer[PATH_BUFFER_SIZE]; 184 185 path = node_get_path(node, buffer, 0); 186 res = lstat(path, &s); 187 if (res < 0) { 188 ERROR("lstat('%s') errno %d\n", path, errno); 189 return -1; 190 } 191 192 attr_from_stat(attr, &s); 193 attr->ino = node->nid; 194 195 return 0; 196} 197 198struct node *node_create(struct node *parent, const char *name, __u64 nid, __u64 gen) 199{ 200 struct node *node; 201 int namelen = strlen(name); 202 203 node = calloc(1, sizeof(struct node) + namelen); 204 if (node == 0) { 205 return 0; 206 } 207 208 node->nid = nid; 209 node->gen = gen; 210 node->parent = parent; 211 node->next = parent->child; 212 parent->child = node; 213 memcpy(node->name, name, namelen + 1); 214 node->namelen = namelen; 215 parent->refcount++; 216 217 return node; 218} 219 220void fuse_init(struct fuse *fuse, int fd, const char *path) 221{ 222 fuse->fd = fd; 223 fuse->next_node_id = 2; 224 fuse->next_generation = 0; 225 226 fuse->all = &fuse->root; 227 228 fuse->root.nid = FUSE_ROOT_ID; /* 1 */ 229 fuse->root.next = 0; 230 fuse->root.child = 0; 231 fuse->root.parent = 0; 232 233 fuse->root.all = 0; 234 fuse->root.refcount = 2; 235 236 strcpy(fuse->root.name, path); 237 fuse->root.namelen = strlen(fuse->root.name); 238} 239 240static inline void *id_to_ptr(__u64 nid) 241{ 242 return (void *) nid; 243} 244 245static inline __u64 ptr_to_id(void *ptr) 246{ 247 return (__u64) ptr; 248} 249 250 251struct node *lookup_by_inode(struct fuse *fuse, __u64 nid) 252{ 253 if (nid == FUSE_ROOT_ID) { 254 return &fuse->root; 255 } else { 256 return id_to_ptr(nid); 257 } 258} 259 260struct node *lookup_child_by_name(struct node *node, const char *name) 261{ 262 for (node = node->child; node; node = node->next) { 263 if (!strcmp(name, node->name)) { 264 return node; 265 } 266 } 267 return 0; 268} 269 270struct node *lookup_child_by_inode(struct node *node, __u64 nid) 271{ 272 for (node = node->child; node; node = node->next) { 273 if (node->nid == nid) { 274 return node; 275 } 276 } 277 return 0; 278} 279 280struct node *node_lookup(struct fuse *fuse, struct node *parent, const char *name, 281 struct fuse_attr *attr) 282{ 283 int res; 284 struct stat s; 285 char *path, buffer[PATH_BUFFER_SIZE]; 286 struct node *node; 287 288 path = node_get_path(parent, buffer, name); 289 /* XXX error? */ 290 291 res = lstat(path, &s); 292 if (res < 0) 293 return 0; 294 295 node = lookup_child_by_name(parent, name); 296 if (!node) { 297 node = node_create(parent, name, fuse->next_node_id++, fuse->next_generation++); 298 if (!node) 299 return 0; 300 node->nid = ptr_to_id(node); 301 node->all = fuse->all; 302 fuse->all = node; 303 } 304 305 attr_from_stat(attr, &s); 306 attr->ino = node->nid; 307 308 return node; 309} 310 311void node_release(struct node *node) 312{ 313 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount); 314 node->refcount--; 315 if (node->refcount == 0) { 316 if (node->parent->child == node) { 317 node->parent->child = node->parent->child->next; 318 } else { 319 struct node *node2; 320 321 node2 = node->parent->child; 322 while (node2->next != node) 323 node2 = node2->next; 324 node2->next = node->next; 325 } 326 327 TRACE("DESTROY %p (%s)\n", node, node->name); 328 329 node_release(node->parent); 330 331 node->parent = 0; 332 node->next = 0; 333 334 /* TODO: remove debugging - poison memory */ 335 memset(node, 0xef, sizeof(*node) + strlen(node->name)); 336 337 free(node); 338 } 339} 340 341void fuse_status(struct fuse *fuse, __u64 unique, int err) 342{ 343 struct fuse_out_header hdr; 344 hdr.len = sizeof(hdr); 345 hdr.error = err; 346 hdr.unique = unique; 347 if (err) { 348// ERROR("*** %d ***\n", err); 349 } 350 write(fuse->fd, &hdr, sizeof(hdr)); 351} 352 353void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len) 354{ 355 struct fuse_out_header hdr; 356 struct iovec vec[2]; 357 int res; 358 359 hdr.len = len + sizeof(hdr); 360 hdr.error = 0; 361 hdr.unique = unique; 362 363 vec[0].iov_base = &hdr; 364 vec[0].iov_len = sizeof(hdr); 365 vec[1].iov_base = data; 366 vec[1].iov_len = len; 367 368 res = writev(fuse->fd, vec, 2); 369 if (res < 0) { 370 ERROR("*** REPLY FAILED *** %d\n", errno); 371 } 372} 373 374void lookup_entry(struct fuse *fuse, struct node *node, 375 const char *name, __u64 unique) 376{ 377 struct fuse_entry_out out; 378 379 memset(&out, 0, sizeof(out)); 380 381 node = node_lookup(fuse, node, name, &out.attr); 382 if (!node) { 383 fuse_status(fuse, unique, -ENOENT); 384 return; 385 } 386 387 node->refcount++; 388// fprintf(stderr,"ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount); 389 out.nodeid = node->nid; 390 out.generation = node->gen; 391 out.entry_valid = 10; 392 out.attr_valid = 10; 393 394 fuse_reply(fuse, unique, &out, sizeof(out)); 395} 396 397void handle_fuse_request(struct fuse *fuse, struct fuse_in_header *hdr, void *data, unsigned len) 398{ 399 struct node *node; 400 401 if ((len < sizeof(*hdr)) || (hdr->len != len)) { 402 ERROR("malformed header\n"); 403 return; 404 } 405 406 len -= hdr->len; 407 408 if (hdr->nodeid) { 409 node = lookup_by_inode(fuse, hdr->nodeid); 410 if (!node) { 411 fuse_status(fuse, hdr->unique, -ENOENT); 412 return; 413 } 414 } else { 415 node = 0; 416 } 417 418 switch (hdr->opcode) { 419 case FUSE_LOOKUP: { /* bytez[] -> entry_out */ 420 TRACE("LOOKUP %llx %s\n", hdr->nodeid, (char*) data); 421 lookup_entry(fuse, node, (char*) data, hdr->unique); 422 return; 423 } 424 case FUSE_FORGET: { 425 struct fuse_forget_in *req = data; 426 TRACE("FORGET %llx (%s) #%lld\n", hdr->nodeid, node->name, req->nlookup); 427 /* no reply */ 428 while (req->nlookup--) 429 node_release(node); 430 return; 431 } 432 case FUSE_GETATTR: { /* getattr_in -> attr_out */ 433 struct fuse_getattr_in *req = data; 434 struct fuse_attr_out out; 435 436 TRACE("GETATTR flags=%x fh=%llx\n",req->getattr_flags, req->fh); 437 438 memset(&out, 0, sizeof(out)); 439 node_get_attr(node, &out.attr); 440 out.attr_valid = 10; 441 442 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 443 return; 444 } 445 case FUSE_SETATTR: { /* setattr_in -> attr_out */ 446 struct fuse_setattr_in *req = data; 447 struct fuse_attr_out out; 448 char *path, buffer[PATH_BUFFER_SIZE]; 449 int res = 0; 450 451 TRACE("SETATTR fh=%llx id=%llx valid=%x\n", 452 req->fh, hdr->nodeid, req->valid); 453 454 /* XXX: incomplete implementation -- truncate only. chmod/chown 455 * should NEVER be implemented. */ 456 457 path = node_get_path(node, buffer, 0); 458 if (req->valid & FATTR_SIZE) 459 res = truncate(path, req->size); 460 461 memset(&out, 0, sizeof(out)); 462 node_get_attr(node, &out.attr); 463 out.attr_valid = 10; 464 465 if (res) 466 fuse_status(fuse, hdr->unique, -errno); 467 else 468 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 469 return; 470 } 471// case FUSE_READLINK: 472// case FUSE_SYMLINK: 473 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */ 474 struct fuse_mknod_in *req = data; 475 char *path, buffer[PATH_BUFFER_SIZE]; 476 char *name = ((char*) data) + sizeof(*req); 477 int res; 478 TRACE("MKNOD %s @ %llx\n", name, hdr->nodeid); 479 path = node_get_path(node, buffer, name); 480 481 req->mode = (req->mode & (~0777)) | 0664; 482 res = mknod(path, req->mode, req->rdev); /* XXX perm?*/ 483 if (res < 0) { 484 fuse_status(fuse, hdr->unique, -errno); 485 } else { 486 lookup_entry(fuse, node, name, hdr->unique); 487 } 488 return; 489 } 490 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */ 491 struct fuse_mkdir_in *req = data; 492 struct fuse_entry_out out; 493 char *path, buffer[PATH_BUFFER_SIZE]; 494 char *name = ((char*) data) + sizeof(*req); 495 int res; 496 TRACE("MKDIR %s @ %llx 0%o\n", name, hdr->nodeid, req->mode); 497 path = node_get_path(node, buffer, name); 498 499 req->mode = (req->mode & (~0777)) | 0775; 500 res = mkdir(path, req->mode); 501 if (res < 0) { 502 fuse_status(fuse, hdr->unique, -errno); 503 } else { 504 lookup_entry(fuse, node, name, hdr->unique); 505 } 506 return; 507 } 508 case FUSE_UNLINK: { /* bytez[] -> */ 509 char *path, buffer[PATH_BUFFER_SIZE]; 510 int res; 511 TRACE("UNLINK %s @ %llx\n", (char*) data, hdr->nodeid); 512 path = node_get_path(node, buffer, (char*) data); 513 res = unlink(path); 514 fuse_status(fuse, hdr->unique, res ? -errno : 0); 515 return; 516 } 517 case FUSE_RMDIR: { /* bytez[] -> */ 518 char *path, buffer[PATH_BUFFER_SIZE]; 519 int res; 520 TRACE("RMDIR %s @ %llx\n", (char*) data, hdr->nodeid); 521 path = node_get_path(node, buffer, (char*) data); 522 res = rmdir(path); 523 fuse_status(fuse, hdr->unique, res ? -errno : 0); 524 return; 525 } 526 case FUSE_RENAME: { /* rename_in, oldname, newname -> */ 527 struct fuse_rename_in *req = data; 528 char *oldname = ((char*) data) + sizeof(*req); 529 char *newname = oldname + strlen(oldname) + 1; 530 char *oldpath, oldbuffer[PATH_BUFFER_SIZE]; 531 char *newpath, newbuffer[PATH_BUFFER_SIZE]; 532 struct node *newnode; 533 int res; 534 535 newnode = lookup_by_inode(fuse, req->newdir); 536 if (!newnode) { 537 fuse_status(fuse, hdr->unique, -ENOENT); 538 return; 539 } 540 541 oldpath = node_get_path(node, oldbuffer, oldname); 542 newpath = node_get_path(newnode, newbuffer, newname); 543 544 res = rename(oldpath, newpath); 545 fuse_status(fuse, hdr->unique, res ? -errno : 0); 546 return; 547 } 548// case FUSE_LINK: 549 case FUSE_OPEN: { /* open_in -> open_out */ 550 struct fuse_open_in *req = data; 551 struct fuse_open_out out; 552 char *path, buffer[PATH_BUFFER_SIZE]; 553 struct handle *h; 554 555 h = malloc(sizeof(*h)); 556 if (!h) { 557 fuse_status(fuse, hdr->unique, -ENOMEM); 558 return; 559 } 560 561 path = node_get_path(node, buffer, 0); 562 TRACE("OPEN %llx '%s' 0%o fh=%p\n", hdr->nodeid, path, req->flags, h); 563 h->fd = open(path, req->flags); 564 if (h->fd < 0) { 565 ERROR("ERROR\n"); 566 fuse_status(fuse, hdr->unique, errno); 567 free(h); 568 return; 569 } 570 out.fh = ptr_to_id(h); 571 out.open_flags = 0; 572 out.padding = 0; 573 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 574 return; 575 } 576 case FUSE_READ: { /* read_in -> byte[] */ 577 char buffer[128 * 1024]; 578 struct fuse_read_in *req = data; 579 struct handle *h = id_to_ptr(req->fh); 580 int res; 581 TRACE("READ %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); 582 if (req->size > sizeof(buffer)) { 583 fuse_status(fuse, hdr->unique, -EINVAL); 584 return; 585 } 586 res = pread(h->fd, buffer, req->size, req->offset); 587 if (res < 0) { 588 fuse_status(fuse, hdr->unique, errno); 589 return; 590 } 591 fuse_reply(fuse, hdr->unique, buffer, res); 592 return; 593 } 594 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */ 595 struct fuse_write_in *req = data; 596 struct fuse_write_out out; 597 struct handle *h = id_to_ptr(req->fh); 598 int res; 599 TRACE("WRITE %p(%d) %u@%llu\n", h, h->fd, req->size, req->offset); 600 res = pwrite(h->fd, ((char*) data) + sizeof(*req), req->size, req->offset); 601 if (res < 0) { 602 fuse_status(fuse, hdr->unique, errno); 603 return; 604 } 605 out.size = res; 606 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 607 goto oops; 608 } 609 case FUSE_STATFS: { /* getattr_in -> attr_out */ 610 struct statfs stat; 611 struct fuse_statfs_out out; 612 int res; 613 614 TRACE("STATFS\n"); 615 616 if (statfs(fuse->root.name, &stat)) { 617 fuse_status(fuse, hdr->unique, -errno); 618 return; 619 } 620 621 memset(&out, 0, sizeof(out)); 622 out.st.blocks = stat.f_blocks; 623 out.st.bfree = stat.f_bfree; 624 out.st.bavail = stat.f_bavail; 625 out.st.files = stat.f_files; 626 out.st.ffree = stat.f_ffree; 627 out.st.bsize = stat.f_bsize; 628 out.st.namelen = stat.f_namelen; 629 out.st.frsize = stat.f_frsize; 630 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 631 return; 632 } 633 case FUSE_RELEASE: { /* release_in -> */ 634 struct fuse_release_in *req = data; 635 struct handle *h = id_to_ptr(req->fh); 636 TRACE("RELEASE %p(%d)\n", h, h->fd); 637 close(h->fd); 638 free(h); 639 fuse_status(fuse, hdr->unique, 0); 640 return; 641 } 642// case FUSE_FSYNC: 643// case FUSE_SETXATTR: 644// case FUSE_GETXATTR: 645// case FUSE_LISTXATTR: 646// case FUSE_REMOVEXATTR: 647 case FUSE_FLUSH: 648 fuse_status(fuse, hdr->unique, 0); 649 return; 650 case FUSE_OPENDIR: { /* open_in -> open_out */ 651 struct fuse_open_in *req = data; 652 struct fuse_open_out out; 653 char *path, buffer[PATH_BUFFER_SIZE]; 654 struct dirhandle *h; 655 656 h = malloc(sizeof(*h)); 657 if (!h) { 658 fuse_status(fuse, hdr->unique, -ENOMEM); 659 return; 660 } 661 662 path = node_get_path(node, buffer, 0); 663 TRACE("OPENDIR %llx '%s'\n", hdr->nodeid, path); 664 h->d = opendir(path); 665 if (h->d == 0) { 666 ERROR("ERROR\n"); 667 fuse_status(fuse, hdr->unique, -errno); 668 free(h); 669 return; 670 } 671 out.fh = ptr_to_id(h); 672 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 673 return; 674 } 675 case FUSE_READDIR: { 676 struct fuse_read_in *req = data; 677 char buffer[8192]; 678 struct fuse_dirent *fde = (struct fuse_dirent*) buffer; 679 struct dirent *de; 680 struct dirhandle *h = id_to_ptr(req->fh); 681 TRACE("READDIR %p\n", h); 682 de = readdir(h->d); 683 if (!de) { 684 fuse_status(fuse, hdr->unique, 0); 685 return; 686 } 687 fde->ino = FUSE_UNKNOWN_INO; 688 fde->off = 0; 689 fde->type = de->d_type; 690 fde->namelen = strlen(de->d_name); 691 memcpy(fde->name, de->d_name, fde->namelen + 1); 692 fuse_reply(fuse, hdr->unique, fde, 693 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen)); 694 return; 695 } 696 case FUSE_RELEASEDIR: { /* release_in -> */ 697 struct fuse_release_in *req = data; 698 struct dirhandle *h = id_to_ptr(req->fh); 699 TRACE("RELEASEDIR %p\n",h); 700 closedir(h->d); 701 free(h); 702 fuse_status(fuse, hdr->unique, 0); 703 return; 704 } 705// case FUSE_FSYNCDIR: 706 case FUSE_INIT: { /* init_in -> init_out */ 707 struct fuse_init_in *req = data; 708 struct fuse_init_out out; 709 710 TRACE("INIT ver=%d.%d maxread=%d flags=%x\n", 711 req->major, req->minor, req->max_readahead, req->flags); 712 713 out.major = FUSE_KERNEL_VERSION; 714 out.minor = FUSE_KERNEL_MINOR_VERSION; 715 out.max_readahead = req->max_readahead; 716 out.flags = FUSE_ATOMIC_O_TRUNC; 717 out.max_background = 32; 718 out.congestion_threshold = 32; 719 out.max_write = 256 * 1024; 720 721 fuse_reply(fuse, hdr->unique, &out, sizeof(out)); 722 return; 723 } 724 default: { 725 struct fuse_out_header h; 726 ERROR("NOTIMPL op=%d uniq=%llx nid=%llx\n", 727 hdr->opcode, hdr->unique, hdr->nodeid); 728 729 oops: 730 h.len = sizeof(h); 731 h.error = -ENOSYS; 732 h.unique = hdr->unique; 733 write(fuse->fd, &h, sizeof(h)); 734 break; 735 } 736 } 737} 738 739void handle_fuse_requests(struct fuse *fuse) 740{ 741 unsigned char req[256 * 1024 + 128]; 742 int len; 743 744 for (;;) { 745 len = read(fuse->fd, req, 8192); 746 if (len < 0) { 747 if (errno == EINTR) 748 continue; 749 ERROR("handle_fuse_requests: errno=%d\n", errno); 750 return; 751 } 752 handle_fuse_request(fuse, (void*) req, (void*) (req + sizeof(struct fuse_in_header)), len); 753 } 754} 755 756int main(int argc, char **argv) 757{ 758 struct fuse fuse; 759 char opts[256]; 760 int fd; 761 int res; 762 unsigned uid; 763 unsigned gid; 764 const char *path; 765 766 if (argc != 4) { 767 ERROR("usage: sdcard <path> <uid> <gid>\n"); 768 return -1; 769 } 770 771 uid = strtoul(argv[2], 0, 10); 772 gid = strtoul(argv[3], 0, 10); 773 if (!uid || !gid) { 774 ERROR("uid and gid must be nonzero\n"); 775 return -1; 776 } 777 778 path = argv[1]; 779 780 /* cleanup from previous instance, if necessary */ 781 umount2(MOUNT_POINT, 2); 782 783 fd = open("/dev/fuse", O_RDWR); 784 if (fd < 0){ 785 ERROR("cannot open fuse device (%d)\n", errno); 786 return -1; 787 } 788 789 sprintf(opts, "fd=%i,rootmode=40000,default_permissions,allow_other," 790 "user_id=%d,group_id=%d", fd, uid, gid); 791 792 res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts); 793 if (res < 0) { 794 ERROR("cannot mount fuse filesystem (%d)\n", errno); 795 return -1; 796 } 797 798 if (setgid(gid) < 0) { 799 ERROR("cannot setgid!\n"); 800 return -1; 801 } 802 if (setuid(uid) < 0) { 803 ERROR("cannot setuid!\n"); 804 return -1; 805 } 806 807 fuse_init(&fuse, fd, path); 808 809 umask(0); 810 handle_fuse_requests(&fuse); 811 812 return 0; 813} 814