blob: 377c008dfbd7e705b87cc0a893a10d885e150968 [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>
Jeff Brown7729d242012-05-25 15:35:28 -070028#include <limits.h>
Mike Lockwood1bedb732011-01-13 13:38:42 -050029#include <ctype.h>
Jeff Brown6249b902012-05-26 14:32:54 -070030#include <pthread.h>
Ken Sumrall2fd72cc2013-02-08 16:50:55 -080031#include <sys/time.h>
32#include <sys/resource.h>
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -070033#include <sys/inotify.h>
34
35#include <cutils/hashmap.h>
36#include <cutils/multiuser.h>
Brian Swetland03ee9472010-08-12 18:01:08 -070037
Brian Swetlandb14a2c62010-08-12 18:21:12 -070038#include <private/android_filesystem_config.h>
39
Brian Swetland03ee9472010-08-12 18:01:08 -070040#include "fuse.h"
41
42/* README
43 *
44 * What is this?
45 *
46 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
47 * directory permissions (all files are given fixed owner, group, and
48 * permissions at creation, owner, group, and permissions are not
49 * changeable, symlinks and hardlinks are not createable, etc.
50 *
Jeff Sharkeye169bd02012-08-13 16:44:42 -070051 * See usage() for command line options.
Brian Swetland03ee9472010-08-12 18:01:08 -070052 *
Jeff Sharkeye169bd02012-08-13 16:44:42 -070053 * It must be run as root, but will drop to requested UID/GID as soon as it
54 * mounts a filesystem. It will refuse to run if requested UID/GID are zero.
Brian Swetland03ee9472010-08-12 18:01:08 -070055 *
56 * Things I believe to be true:
57 *
58 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
59 * CREAT) must bump that node's refcount
60 * - don't forget that FORGET can forget multiple references (req->nlookup)
61 * - if an op that returns a fuse_entry fails writing the reply to the
62 * kernel, you must rollback the refcount to reflect the reference the
63 * kernel did not actually acquire
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -070064 *
65 * This daemon can also derive custom filesystem permissions based on directory
66 * structure when requested. These custom permissions support several features:
67 *
68 * - Apps can access their own files in /Android/data/com.example/ without
69 * requiring any additional GIDs.
70 * - Separate permissions for protecting directories like Pictures and Music.
71 * - Multi-user separation on the same physical device.
72 *
73 * The derived permissions look like this:
74 *
75 * rwxrwx--x root:sdcard_rw /
76 * rwxrwx--- root:sdcard_pics /Pictures
77 * rwxrwx--- root:sdcard_av /Music
78 *
79 * rwxrwx--x root:sdcard_rw /Android
80 * rwxrwx--x root:sdcard_rw /Android/data
81 * rwxrwx--- u0_a12:sdcard_rw /Android/data/com.example
82 * rwxrwx--x root:sdcard_rw /Android/obb/
83 * rwxrwx--- u0_a12:sdcard_rw /Android/obb/com.example
84 *
85 * rwxrwx--- root:sdcard_all /Android/user
86 * rwxrwx--x root:sdcard_rw /Android/user/10
87 * rwxrwx--- u10_a12:sdcard_rw /Android/user/10/Android/data/com.example
Brian Swetland03ee9472010-08-12 18:01:08 -070088 */
89
90#define FUSE_TRACE 0
91
92#if FUSE_TRACE
93#define TRACE(x...) fprintf(stderr,x)
94#else
95#define TRACE(x...) do {} while (0)
96#endif
97
98#define ERROR(x...) fprintf(stderr,x)
99
100#define FUSE_UNKNOWN_INO 0xffffffff
101
Jeff Brown84715842012-05-25 14:07:47 -0700102/* Maximum number of bytes to write in one request. */
103#define MAX_WRITE (256 * 1024)
104
105/* Maximum number of bytes to read in one request. */
106#define MAX_READ (128 * 1024)
107
108/* Largest possible request.
109 * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
110 * the largest possible data payload. */
111#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
112
Jeff Brown6249b902012-05-26 14:32:54 -0700113/* Default number of threads. */
114#define DEFAULT_NUM_THREADS 2
115
116/* Pseudo-error constant used to indicate that no fuse status is needed
117 * or that a reply has already been written. */
118#define NO_STATUS 1
119
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700120/* Path to system-provided mapping of package name to appIds */
121static const char* const kPackagesListFile = "/data/system/packages.list";
122
123/* Supplementary groups to execute with */
124static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
125
126/* Permission mode for a specific node. Controls how file permissions
127 * are derived for children nodes. */
128typedef enum {
129 PERM_INHERIT,
130 PERM_ROOT,
131 PERM_ANDROID,
132 PERM_ANDROID_DATA,
133 PERM_ANDROID_OBB,
134 PERM_ANDROID_USER,
135} perm_t;
136
Brian Swetland03ee9472010-08-12 18:01:08 -0700137struct handle {
Brian Swetland03ee9472010-08-12 18:01:08 -0700138 int fd;
139};
140
141struct dirhandle {
Brian Swetland03ee9472010-08-12 18:01:08 -0700142 DIR *d;
143};
144
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700145struct package {
146 appid_t appid;
147};
148
Brian Swetland03ee9472010-08-12 18:01:08 -0700149struct node {
Jeff Brown6249b902012-05-26 14:32:54 -0700150 __u32 refcount;
Brian Swetland03ee9472010-08-12 18:01:08 -0700151 __u64 nid;
152 __u64 gen;
153
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700154 /* State derived based on current position in hierarchy. */
155 perm_t perm;
156 userid_t userid;
157 uid_t uid;
158 gid_t gid;
159 mode_t mode;
160
Paul Eastham11ccdb32010-10-14 11:04:26 -0700161 struct node *next; /* per-dir sibling list */
162 struct node *child; /* first contained file by this dir */
Paul Eastham11ccdb32010-10-14 11:04:26 -0700163 struct node *parent; /* containing directory */
Brian Swetland03ee9472010-08-12 18:01:08 -0700164
Jeff Brown6249b902012-05-26 14:32:54 -0700165 size_t namelen;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700166 char *name;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800167 /* If non-null, this is the real name of the file in the underlying storage.
168 * This may differ from the field "name" only by case.
169 * strlen(actual_name) will always equal strlen(name), so it is safe to use
170 * namelen for both fields.
171 */
172 char *actual_name;
Brian Swetland03ee9472010-08-12 18:01:08 -0700173};
174
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700175static int str_hash(void *key) {
176 return hashmapHash(key, strlen(key));
177}
178
179static bool str_equals(void *keyA, void *keyB) {
180 return strcmp(keyA, keyB) == 0;
181}
182
Jeff Brown7729d242012-05-25 15:35:28 -0700183/* Global data structure shared by all fuse handlers. */
Brian Swetland03ee9472010-08-12 18:01:08 -0700184struct fuse {
Jeff Brown6249b902012-05-26 14:32:54 -0700185 pthread_mutex_t lock;
186
Brian Swetland03ee9472010-08-12 18:01:08 -0700187 __u64 next_generation;
Brian Swetland03ee9472010-08-12 18:01:08 -0700188 int fd;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700189 bool derive_perms;
Brian Swetland03ee9472010-08-12 18:01:08 -0700190 struct node root;
Jeff Brown7729d242012-05-25 15:35:28 -0700191 char rootpath[PATH_MAX];
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700192
193 Hashmap *package_to_appid;
Brian Swetland03ee9472010-08-12 18:01:08 -0700194};
195
Jeff Brown7729d242012-05-25 15:35:28 -0700196/* Private data used by a single fuse handler. */
197struct fuse_handler {
Jeff Brown6249b902012-05-26 14:32:54 -0700198 struct fuse* fuse;
199 int token;
200
Jeff Brown7729d242012-05-25 15:35:28 -0700201 /* To save memory, we never use the contents of the request buffer and the read
202 * buffer at the same time. This allows us to share the underlying storage. */
203 union {
204 __u8 request_buffer[MAX_REQUEST_SIZE];
205 __u8 read_buffer[MAX_READ];
206 };
207};
Brian Swetland03ee9472010-08-12 18:01:08 -0700208
Jeff Brown6249b902012-05-26 14:32:54 -0700209static inline void *id_to_ptr(__u64 nid)
Brian Swetland03ee9472010-08-12 18:01:08 -0700210{
Jeff Brown6249b902012-05-26 14:32:54 -0700211 return (void *) (uintptr_t) nid;
212}
Brian Swetland03ee9472010-08-12 18:01:08 -0700213
Jeff Brown6249b902012-05-26 14:32:54 -0700214static inline __u64 ptr_to_id(void *ptr)
215{
216 return (__u64) (uintptr_t) ptr;
217}
Brian Swetland03ee9472010-08-12 18:01:08 -0700218
Jeff Brown6249b902012-05-26 14:32:54 -0700219static void acquire_node_locked(struct node* node)
220{
221 node->refcount++;
222 TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
223}
224
225static void remove_node_from_parent_locked(struct node* node);
226
227static void release_node_locked(struct node* node)
228{
229 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
230 if (node->refcount > 0) {
231 node->refcount--;
232 if (!node->refcount) {
233 TRACE("DESTROY %p (%s)\n", node, node->name);
234 remove_node_from_parent_locked(node);
235
236 /* TODO: remove debugging - poison memory */
237 memset(node->name, 0xef, node->namelen);
238 free(node->name);
239 free(node->actual_name);
240 memset(node, 0xfc, sizeof(*node));
241 free(node);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800242 }
Jeff Brown6249b902012-05-26 14:32:54 -0700243 } else {
244 ERROR("Zero refcnt %p\n", node);
245 }
246}
247
248static void add_node_to_parent_locked(struct node *node, struct node *parent) {
249 node->parent = parent;
250 node->next = parent->child;
251 parent->child = node;
252 acquire_node_locked(parent);
253}
254
255static void remove_node_from_parent_locked(struct node* node)
256{
257 if (node->parent) {
258 if (node->parent->child == node) {
259 node->parent->child = node->parent->child->next;
260 } else {
261 struct node *node2;
262 node2 = node->parent->child;
263 while (node2->next != node)
264 node2 = node2->next;
265 node2->next = node->next;
266 }
267 release_node_locked(node->parent);
268 node->parent = NULL;
269 node->next = NULL;
270 }
271}
272
273/* Gets the absolute path to a node into the provided buffer.
274 *
275 * Populates 'buf' with the path and returns the length of the path on success,
276 * or returns -1 if the path is too long for the provided buffer.
277 */
278static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize)
279{
280 size_t namelen = node->namelen;
281 if (bufsize < namelen + 1) {
282 return -1;
Brian Swetland03ee9472010-08-12 18:01:08 -0700283 }
284
Jeff Brown6249b902012-05-26 14:32:54 -0700285 ssize_t pathlen = 0;
286 if (node->parent) {
287 pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
288 if (pathlen < 0) {
289 return -1;
290 }
291 buf[pathlen++] = '/';
292 }
293
294 const char* name = node->actual_name ? node->actual_name : node->name;
295 memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
296 return pathlen + namelen;
297}
298
299/* Finds the absolute path of a file within a given directory.
300 * Performs a case-insensitive search for the file and sets the buffer to the path
301 * of the first matching file. If 'search' is zero or if no match is found, sets
302 * the buffer to the path that the file would have, assuming the name were case-sensitive.
303 *
304 * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
305 * or returns NULL if the path is too long for the provided buffer.
306 */
307static char* find_file_within(const char* path, const char* name,
308 char* buf, size_t bufsize, int search)
309{
310 size_t pathlen = strlen(path);
311 size_t namelen = strlen(name);
312 size_t childlen = pathlen + namelen + 1;
313 char* actual;
314
315 if (bufsize <= childlen) {
316 return NULL;
317 }
318
319 memcpy(buf, path, pathlen);
320 buf[pathlen] = '/';
321 actual = buf + pathlen + 1;
322 memcpy(actual, name, namelen + 1);
323
324 if (search && access(buf, F_OK)) {
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800325 struct dirent* entry;
Jeff Brown6249b902012-05-26 14:32:54 -0700326 DIR* dir = opendir(path);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800327 if (!dir) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700328 ERROR("opendir %s failed: %s\n", path, strerror(errno));
Jeff Brown6249b902012-05-26 14:32:54 -0700329 return actual;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800330 }
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800331 while ((entry = readdir(dir))) {
Jeff Brown6249b902012-05-26 14:32:54 -0700332 if (!strcasecmp(entry->d_name, name)) {
333 /* we have a match - replace the name, don't need to copy the null again */
334 memcpy(actual, entry->d_name, namelen);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800335 break;
336 }
337 }
338 closedir(dir);
339 }
Jeff Brown6249b902012-05-26 14:32:54 -0700340 return actual;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800341}
342
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700343static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, const struct node* node)
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800344{
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700345 attr->ino = node->nid;
Brian Swetland03ee9472010-08-12 18:01:08 -0700346 attr->size = s->st_size;
347 attr->blocks = s->st_blocks;
Mike Lockwood4553b082010-08-16 14:14:44 -0400348 attr->atime = s->st_atime;
349 attr->mtime = s->st_mtime;
350 attr->ctime = s->st_ctime;
351 attr->atimensec = s->st_atime_nsec;
352 attr->mtimensec = s->st_mtime_nsec;
353 attr->ctimensec = s->st_ctime_nsec;
Brian Swetland03ee9472010-08-12 18:01:08 -0700354 attr->mode = s->st_mode;
355 attr->nlink = s->st_nlink;
Brian Swetland03ee9472010-08-12 18:01:08 -0700356
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700357 attr->uid = node->uid;
358 attr->gid = node->gid;
359
360 /* Filter requested mode based on underlying file, and
361 * pass through file type. */
362 int owner_mode = s->st_mode & 0700;
363 int filtered_mode = node->mode & (owner_mode | (owner_mode >> 3) | (owner_mode >> 6));
364 attr->mode = (attr->mode & S_IFMT) | filtered_mode;
365}
366
367static void derive_permissions_locked(struct fuse* fuse, struct node *parent,
368 struct node *node) {
369 struct package* package;
370
371 /* By default, each node inherits from its parent */
372 node->perm = PERM_INHERIT;
373 node->userid = parent->userid;
374 node->uid = parent->uid;
375 node->gid = parent->gid;
376 node->mode = parent->mode;
377
378 if (!fuse->derive_perms) {
379 return;
Brian Swetlandb14a2c62010-08-12 18:21:12 -0700380 }
381
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700382 /* Derive custom permissions based on parent and current node */
383 switch (parent->perm) {
384 case PERM_INHERIT:
385 /* Already inherited above */
386 break;
387 case PERM_ROOT:
388 if (!strcmp(node->name, "Android")) {
389 /* App-specific directories inside; let anyone traverse */
390 node->perm = PERM_ANDROID;
391 node->mode = 0771;
392 } else if (!strcmp(node->name, "DCIM")
393 || !strcmp(node->name, "Pictures")) {
394 node->gid = AID_SDCARD_PICS;
395 node->mode = 0770;
396 } else if (!strcmp(node->name, "Alarms")
397 || !strcmp(node->name, "Movies")
398 || !strcmp(node->name, "Music")
399 || !strcmp(node->name, "Notifications")
400 || !strcmp(node->name, "Podcasts")
401 || !strcmp(node->name, "Ringtones")) {
402 node->gid = AID_SDCARD_AV;
403 node->mode = 0770;
404 }
405 break;
406 case PERM_ANDROID:
407 if (!strcmp(node->name, "data")) {
408 /* App-specific directories inside; let anyone traverse */
409 node->perm = PERM_ANDROID_DATA;
410 node->mode = 0771;
411 } else if (!strcmp(node->name, "obb")) {
412 /* App-specific directories inside; let anyone traverse */
413 node->perm = PERM_ANDROID_OBB;
414 node->mode = 0771;
415 } else if (!strcmp(node->name, "user")) {
416 /* User directories must only be accessible to system, protected
417 * by sdcard_all. Zygote will bind mount the appropriate user-
418 * specific path. */
419 node->perm = PERM_ANDROID_USER;
420 node->gid = AID_SDCARD_ALL;
421 node->mode = 0770;
422 }
423 break;
424 case PERM_ANDROID_DATA:
425 case PERM_ANDROID_OBB:
426 package = hashmapGet(fuse->package_to_appid, node->name);
427 if (package != NULL) {
428 node->uid = multiuser_get_uid(parent->userid, package->appid);
429 }
430 node->mode = 0770;
431 break;
432 case PERM_ANDROID_USER:
433 /* Root of a secondary user */
434 node->perm = PERM_ROOT;
435 node->userid = atoi(node->name);
436 node->gid = AID_SDCARD_RW;
437 node->mode = 0771;
438 break;
439 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700440}
441
Jeff Brown6249b902012-05-26 14:32:54 -0700442struct node *create_node_locked(struct fuse* fuse,
443 struct node *parent, const char *name, const char* actual_name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700444{
445 struct node *node;
Jeff Brown6249b902012-05-26 14:32:54 -0700446 size_t namelen = strlen(name);
Brian Swetland03ee9472010-08-12 18:01:08 -0700447
Paul Eastham11ccdb32010-10-14 11:04:26 -0700448 node = calloc(1, sizeof(struct node));
Jeff Brown6249b902012-05-26 14:32:54 -0700449 if (!node) {
450 return NULL;
Brian Swetland03ee9472010-08-12 18:01:08 -0700451 }
Paul Eastham11ccdb32010-10-14 11:04:26 -0700452 node->name = malloc(namelen + 1);
Jeff Brown6249b902012-05-26 14:32:54 -0700453 if (!node->name) {
Paul Eastham11ccdb32010-10-14 11:04:26 -0700454 free(node);
Jeff Brown6249b902012-05-26 14:32:54 -0700455 return NULL;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700456 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700457 memcpy(node->name, name, namelen + 1);
Jeff Brown6249b902012-05-26 14:32:54 -0700458 if (strcmp(name, actual_name)) {
459 node->actual_name = malloc(namelen + 1);
460 if (!node->actual_name) {
461 free(node->name);
462 free(node);
463 return NULL;
464 }
465 memcpy(node->actual_name, actual_name, namelen + 1);
466 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700467 node->namelen = namelen;
Jeff Brown6249b902012-05-26 14:32:54 -0700468 node->nid = ptr_to_id(node);
469 node->gen = fuse->next_generation++;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700470
471 derive_permissions_locked(fuse, parent, node);
Jeff Brown6249b902012-05-26 14:32:54 -0700472 acquire_node_locked(node);
473 add_node_to_parent_locked(node, parent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700474 return node;
475}
476
Jeff Brown6249b902012-05-26 14:32:54 -0700477static int rename_node_locked(struct node *node, const char *name,
478 const char* actual_name)
Paul Eastham11ccdb32010-10-14 11:04:26 -0700479{
Jeff Brown6249b902012-05-26 14:32:54 -0700480 size_t namelen = strlen(name);
481 int need_actual_name = strcmp(name, actual_name);
482
483 /* make the storage bigger without actually changing the name
484 * in case an error occurs part way */
485 if (namelen > node->namelen) {
486 char* new_name = realloc(node->name, namelen + 1);
487 if (!new_name) {
488 return -ENOMEM;
489 }
490 node->name = new_name;
491 if (need_actual_name && node->actual_name) {
492 char* new_actual_name = realloc(node->actual_name, namelen + 1);
493 if (!new_actual_name) {
494 return -ENOMEM;
495 }
496 node->actual_name = new_actual_name;
497 }
498 }
499
500 /* update the name, taking care to allocate storage before overwriting the old name */
501 if (need_actual_name) {
502 if (!node->actual_name) {
503 node->actual_name = malloc(namelen + 1);
504 if (!node->actual_name) {
505 return -ENOMEM;
506 }
507 }
508 memcpy(node->actual_name, actual_name, namelen + 1);
509 } else {
510 free(node->actual_name);
511 node->actual_name = NULL;
512 }
513 memcpy(node->name, name, namelen + 1);
514 node->namelen = namelen;
515 return 0;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700516}
517
Jeff Brown6249b902012-05-26 14:32:54 -0700518static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
Brian Swetland03ee9472010-08-12 18:01:08 -0700519{
Jeff Brown6249b902012-05-26 14:32:54 -0700520 if (nid == FUSE_ROOT_ID) {
Brian Swetland03ee9472010-08-12 18:01:08 -0700521 return &fuse->root;
522 } else {
523 return id_to_ptr(nid);
524 }
525}
526
Jeff Brown6249b902012-05-26 14:32:54 -0700527static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
528 char* buf, size_t bufsize)
529{
530 struct node* node = lookup_node_by_id_locked(fuse, nid);
531 if (node && get_node_path_locked(node, buf, bufsize) < 0) {
532 node = NULL;
533 }
534 return node;
535}
536
537static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700538{
539 for (node = node->child; node; node = node->next) {
Jeff Brown6249b902012-05-26 14:32:54 -0700540 /* use exact string comparison, nodes that differ by case
541 * must be considered distinct even if they refer to the same
542 * underlying file as otherwise operations such as "mv x x"
543 * will not work because the source and target nodes are the same. */
Brian Swetland03ee9472010-08-12 18:01:08 -0700544 if (!strcmp(name, node->name)) {
545 return node;
546 }
547 }
548 return 0;
549}
550
Jeff Brown6249b902012-05-26 14:32:54 -0700551static struct node* acquire_or_create_child_locked(
552 struct fuse* fuse, struct node* parent,
553 const char* name, const char* actual_name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700554{
Jeff Brown6249b902012-05-26 14:32:54 -0700555 struct node* child = lookup_child_by_name_locked(parent, name);
556 if (child) {
557 acquire_node_locked(child);
Paul Eastham77085c52011-01-04 21:06:03 -0800558 } else {
Jeff Brown6249b902012-05-26 14:32:54 -0700559 child = create_node_locked(fuse, parent, name, actual_name);
Paul Eastham77085c52011-01-04 21:06:03 -0800560 }
Jeff Brown6249b902012-05-26 14:32:54 -0700561 return child;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700562}
563
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700564static void fuse_init(struct fuse *fuse, int fd, const char *source_path, bool derive_perms)
Brian Swetland03ee9472010-08-12 18:01:08 -0700565{
Jeff Brown6249b902012-05-26 14:32:54 -0700566 pthread_mutex_init(&fuse->lock, NULL);
Brian Swetland03ee9472010-08-12 18:01:08 -0700567
Jeff Brown6249b902012-05-26 14:32:54 -0700568 fuse->fd = fd;
569 fuse->next_generation = 0;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700570 fuse->derive_perms = derive_perms;
Brian Swetland03ee9472010-08-12 18:01:08 -0700571
Jeff Brown6249b902012-05-26 14:32:54 -0700572 memset(&fuse->root, 0, sizeof(fuse->root));
573 fuse->root.nid = FUSE_ROOT_ID; /* 1 */
574 fuse->root.refcount = 2;
Jeff Sharkeye169bd02012-08-13 16:44:42 -0700575 fuse->root.namelen = strlen(source_path);
576 fuse->root.name = strdup(source_path);
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700577
578 fuse->root.perm = PERM_ROOT;
579 fuse->root.userid = 0;
580 fuse->root.uid = AID_ROOT;
581 fuse->root.gid = AID_SDCARD_RW;
582 if (derive_perms) {
583 fuse->root.mode = 0771;
584 } else {
585 fuse->root.mode = 0775;
586 }
587
588 if (derive_perms) {
589 fuse->package_to_appid = hashmapCreate(256, str_hash, str_equals);
590 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700591}
592
Jeff Brown6249b902012-05-26 14:32:54 -0700593static void fuse_status(struct fuse *fuse, __u64 unique, int err)
Brian Swetland03ee9472010-08-12 18:01:08 -0700594{
595 struct fuse_out_header hdr;
596 hdr.len = sizeof(hdr);
597 hdr.error = err;
598 hdr.unique = unique;
Brian Swetland03ee9472010-08-12 18:01:08 -0700599 write(fuse->fd, &hdr, sizeof(hdr));
600}
601
Jeff Brown6249b902012-05-26 14:32:54 -0700602static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
Brian Swetland03ee9472010-08-12 18:01:08 -0700603{
604 struct fuse_out_header hdr;
605 struct iovec vec[2];
606 int res;
607
608 hdr.len = len + sizeof(hdr);
609 hdr.error = 0;
610 hdr.unique = unique;
611
612 vec[0].iov_base = &hdr;
613 vec[0].iov_len = sizeof(hdr);
614 vec[1].iov_base = data;
615 vec[1].iov_len = len;
616
617 res = writev(fuse->fd, vec, 2);
618 if (res < 0) {
619 ERROR("*** REPLY FAILED *** %d\n", errno);
620 }
621}
622
Jeff Brown6249b902012-05-26 14:32:54 -0700623static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
624 struct node* parent, const char* name, const char* actual_name,
625 const char* path)
Brian Swetland03ee9472010-08-12 18:01:08 -0700626{
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700627 struct node* node;
Jeff Brown6249b902012-05-26 14:32:54 -0700628 struct fuse_entry_out out;
629 struct stat s;
Brian Swetland03ee9472010-08-12 18:01:08 -0700630
Jeff Brown6249b902012-05-26 14:32:54 -0700631 if (lstat(path, &s) < 0) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700632 /* But wait! We'll automatically create a directory if its
633 * a valid package name under data or obb, since apps may not
634 * have enough permissions to create for themselves. */
635 if (errno == ENOENT && (parent->perm == PERM_ANDROID_DATA
636 || parent->perm == PERM_ANDROID_OBB)) {
637 TRACE("automatically creating %s\n", path);
638
639 pthread_mutex_lock(&fuse->lock);
640 bool validPackage = hashmapContainsKey(fuse->package_to_appid, name);
641 pthread_mutex_unlock(&fuse->lock);
642
643 if (!validPackage) {
644 return -ENOENT;
645 }
646 if (mkdir(path, 0775) == -1) {
647 /* We might have raced with ourselves and already created */
648 if (errno != EEXIST) {
649 ERROR("failed to mkdir(%s): %s\n", name, strerror(errno));
650 return -ENOENT;
651 }
652 }
653
654 /* It should exist this time around! */
655 if (lstat(path, &s) < 0) {
656 ERROR("failed to lstat(%s): %s\n", name, strerror(errno));
657 return -errno;
658 }
659 } else {
660 return -errno;
661 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700662 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700663
Jeff Brown6249b902012-05-26 14:32:54 -0700664 pthread_mutex_lock(&fuse->lock);
665 node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
666 if (!node) {
667 pthread_mutex_unlock(&fuse->lock);
668 return -ENOMEM;
669 }
670 memset(&out, 0, sizeof(out));
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700671 attr_from_stat(&out.attr, &s, node);
Jeff Brown6249b902012-05-26 14:32:54 -0700672 out.attr_valid = 10;
673 out.entry_valid = 10;
Brian Swetland03ee9472010-08-12 18:01:08 -0700674 out.nodeid = node->nid;
675 out.generation = node->gen;
Jeff Brown6249b902012-05-26 14:32:54 -0700676 pthread_mutex_unlock(&fuse->lock);
Brian Swetland03ee9472010-08-12 18:01:08 -0700677 fuse_reply(fuse, unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700678 return NO_STATUS;
Brian Swetland03ee9472010-08-12 18:01:08 -0700679}
680
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700681static int fuse_reply_attr(struct fuse* fuse, __u64 unique, const struct node* node,
Jeff Brown6249b902012-05-26 14:32:54 -0700682 const char* path)
683{
684 struct fuse_attr_out out;
685 struct stat s;
686
687 if (lstat(path, &s) < 0) {
688 return -errno;
689 }
690 memset(&out, 0, sizeof(out));
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700691 attr_from_stat(&out.attr, &s, node);
Jeff Brown6249b902012-05-26 14:32:54 -0700692 out.attr_valid = 10;
693 fuse_reply(fuse, unique, &out, sizeof(out));
694 return NO_STATUS;
695}
696
697static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700698 const struct fuse_in_header *hdr, const char* name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700699{
Jeff Brown6249b902012-05-26 14:32:54 -0700700 struct node* parent_node;
701 char parent_path[PATH_MAX];
702 char child_path[PATH_MAX];
703 const char* actual_name;
Brian Swetland03ee9472010-08-12 18:01:08 -0700704
Jeff Brown6249b902012-05-26 14:32:54 -0700705 pthread_mutex_lock(&fuse->lock);
706 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
707 parent_path, sizeof(parent_path));
708 TRACE("[%d] LOOKUP %s @ %llx (%s)\n", handler->token, name, hdr->nodeid,
709 parent_node ? parent_node->name : "?");
710 pthread_mutex_unlock(&fuse->lock);
711
712 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
713 child_path, sizeof(child_path), 1))) {
714 return -ENOENT;
Brian Swetland03ee9472010-08-12 18:01:08 -0700715 }
Jeff Brown6249b902012-05-26 14:32:54 -0700716 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700717}
718
Jeff Brown6249b902012-05-26 14:32:54 -0700719static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700720 const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
721{
Jeff Brown6249b902012-05-26 14:32:54 -0700722 struct node* node;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700723
Jeff Brown6249b902012-05-26 14:32:54 -0700724 pthread_mutex_lock(&fuse->lock);
725 node = lookup_node_by_id_locked(fuse, hdr->nodeid);
726 TRACE("[%d] FORGET #%lld @ %llx (%s)\n", handler->token, req->nlookup,
727 hdr->nodeid, node ? node->name : "?");
728 if (node) {
729 __u64 n = req->nlookup;
730 while (n--) {
731 release_node_locked(node);
732 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700733 }
Jeff Brown6249b902012-05-26 14:32:54 -0700734 pthread_mutex_unlock(&fuse->lock);
735 return NO_STATUS; /* no reply */
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700736}
737
Jeff Brown6249b902012-05-26 14:32:54 -0700738static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700739 const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
740{
Jeff Brown6249b902012-05-26 14:32:54 -0700741 struct node* node;
742 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700743
Jeff Brown6249b902012-05-26 14:32:54 -0700744 pthread_mutex_lock(&fuse->lock);
745 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
746 TRACE("[%d] GETATTR flags=%x fh=%llx @ %llx (%s)\n", handler->token,
747 req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
748 pthread_mutex_unlock(&fuse->lock);
749
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700750 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -0700751 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700752 }
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700753 return fuse_reply_attr(fuse, hdr->unique, node, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700754}
755
Jeff Brown6249b902012-05-26 14:32:54 -0700756static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700757 const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
758{
Jeff Brown6249b902012-05-26 14:32:54 -0700759 struct node* node;
760 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700761 struct timespec times[2];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700762
Jeff Brown6249b902012-05-26 14:32:54 -0700763 pthread_mutex_lock(&fuse->lock);
764 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
765 TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token,
766 req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
767 pthread_mutex_unlock(&fuse->lock);
768
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700769 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -0700770 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700771 }
772
Jeff Brown6249b902012-05-26 14:32:54 -0700773 /* XXX: incomplete implementation on purpose.
774 * chmod/chown should NEVER be implemented.*/
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700775
Jeff Brown6249b902012-05-26 14:32:54 -0700776 if ((req->valid & FATTR_SIZE) && truncate(path, req->size) < 0) {
777 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700778 }
779
780 /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
781 * are both set, then set it to the current time. Else, set it to the
782 * time specified in the request. Same goes for mtime. Use utimensat(2)
783 * as it allows ATIME and MTIME to be changed independently, and has
784 * nanosecond resolution which fuse also has.
785 */
786 if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
787 times[0].tv_nsec = UTIME_OMIT;
788 times[1].tv_nsec = UTIME_OMIT;
789 if (req->valid & FATTR_ATIME) {
790 if (req->valid & FATTR_ATIME_NOW) {
791 times[0].tv_nsec = UTIME_NOW;
792 } else {
793 times[0].tv_sec = req->atime;
794 times[0].tv_nsec = req->atimensec;
795 }
796 }
797 if (req->valid & FATTR_MTIME) {
798 if (req->valid & FATTR_MTIME_NOW) {
799 times[1].tv_nsec = UTIME_NOW;
800 } else {
801 times[1].tv_sec = req->mtime;
802 times[1].tv_nsec = req->mtimensec;
803 }
804 }
Jeff Brown6249b902012-05-26 14:32:54 -0700805 TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n",
806 handler->token, path, times[0].tv_sec, times[1].tv_sec);
807 if (utimensat(-1, path, times, 0) < 0) {
808 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700809 }
810 }
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -0700811 return fuse_reply_attr(fuse, hdr->unique, node, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700812}
813
Jeff Brown6249b902012-05-26 14:32:54 -0700814static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700815 const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
816{
Jeff Brown6249b902012-05-26 14:32:54 -0700817 struct node* parent_node;
818 char parent_path[PATH_MAX];
819 char child_path[PATH_MAX];
820 const char* actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700821
Jeff Brown6249b902012-05-26 14:32:54 -0700822 pthread_mutex_lock(&fuse->lock);
823 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
824 parent_path, sizeof(parent_path));
825 TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token,
826 name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
827 pthread_mutex_unlock(&fuse->lock);
828
829 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
830 child_path, sizeof(child_path), 1))) {
831 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700832 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700833 __u32 mode = (req->mode & (~0777)) | 0664;
Jeff Brown6249b902012-05-26 14:32:54 -0700834 if (mknod(child_path, mode, req->rdev) < 0) {
835 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700836 }
Jeff Brown6249b902012-05-26 14:32:54 -0700837 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700838}
839
Jeff Brown6249b902012-05-26 14:32:54 -0700840static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700841 const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
842{
Jeff Brown6249b902012-05-26 14:32:54 -0700843 struct node* parent_node;
844 char parent_path[PATH_MAX];
845 char child_path[PATH_MAX];
846 const char* actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700847
Jeff Brown6249b902012-05-26 14:32:54 -0700848 pthread_mutex_lock(&fuse->lock);
849 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
850 parent_path, sizeof(parent_path));
851 TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token,
852 name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
853 pthread_mutex_unlock(&fuse->lock);
854
855 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
856 child_path, sizeof(child_path), 1))) {
857 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700858 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700859 __u32 mode = (req->mode & (~0777)) | 0775;
Jeff Brown6249b902012-05-26 14:32:54 -0700860 if (mkdir(child_path, mode) < 0) {
861 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700862 }
Jeff Brown6249b902012-05-26 14:32:54 -0700863 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700864}
865
Jeff Brown6249b902012-05-26 14:32:54 -0700866static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700867 const struct fuse_in_header* hdr, const char* name)
868{
Jeff Brown6249b902012-05-26 14:32:54 -0700869 struct node* parent_node;
870 char parent_path[PATH_MAX];
871 char child_path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700872
Jeff Brown6249b902012-05-26 14:32:54 -0700873 pthread_mutex_lock(&fuse->lock);
874 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
875 parent_path, sizeof(parent_path));
876 TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token,
877 name, hdr->nodeid, parent_node ? parent_node->name : "?");
878 pthread_mutex_unlock(&fuse->lock);
879
880 if (!parent_node || !find_file_within(parent_path, name,
881 child_path, sizeof(child_path), 1)) {
882 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700883 }
Jeff Brown6249b902012-05-26 14:32:54 -0700884 if (unlink(child_path) < 0) {
885 return -errno;
886 }
887 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700888}
889
Jeff Brown6249b902012-05-26 14:32:54 -0700890static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700891 const struct fuse_in_header* hdr, const char* name)
892{
Jeff Brown6249b902012-05-26 14:32:54 -0700893 struct node* parent_node;
894 char parent_path[PATH_MAX];
895 char child_path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700896
Jeff Brown6249b902012-05-26 14:32:54 -0700897 pthread_mutex_lock(&fuse->lock);
898 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
899 parent_path, sizeof(parent_path));
900 TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token,
901 name, hdr->nodeid, parent_node ? parent_node->name : "?");
902 pthread_mutex_unlock(&fuse->lock);
903
904 if (!parent_node || !find_file_within(parent_path, name,
905 child_path, sizeof(child_path), 1)) {
906 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700907 }
Jeff Brown6249b902012-05-26 14:32:54 -0700908 if (rmdir(child_path) < 0) {
909 return -errno;
910 }
911 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700912}
913
Jeff Brown6249b902012-05-26 14:32:54 -0700914static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700915 const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
Jeff Brown6249b902012-05-26 14:32:54 -0700916 const char* old_name, const char* new_name)
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700917{
Jeff Brown6249b902012-05-26 14:32:54 -0700918 struct node* old_parent_node;
919 struct node* new_parent_node;
920 struct node* child_node;
921 char old_parent_path[PATH_MAX];
922 char new_parent_path[PATH_MAX];
923 char old_child_path[PATH_MAX];
924 char new_child_path[PATH_MAX];
925 const char* new_actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700926 int res;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700927
Jeff Brown6249b902012-05-26 14:32:54 -0700928 pthread_mutex_lock(&fuse->lock);
929 old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
930 old_parent_path, sizeof(old_parent_path));
931 new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
932 new_parent_path, sizeof(new_parent_path));
933 TRACE("[%d] RENAME %s->%s @ %llx (%s) -> %llx (%s)\n", handler->token,
934 old_name, new_name,
935 hdr->nodeid, old_parent_node ? old_parent_node->name : "?",
936 req->newdir, new_parent_node ? new_parent_node->name : "?");
937 if (!old_parent_node || !new_parent_node) {
938 res = -ENOENT;
939 goto lookup_error;
940 }
941 child_node = lookup_child_by_name_locked(old_parent_node, old_name);
942 if (!child_node || get_node_path_locked(child_node,
943 old_child_path, sizeof(old_child_path)) < 0) {
944 res = -ENOENT;
945 goto lookup_error;
946 }
947 acquire_node_locked(child_node);
948 pthread_mutex_unlock(&fuse->lock);
949
950 /* Special case for renaming a file where destination is same path
951 * differing only by case. In this case we don't want to look for a case
952 * insensitive match. This allows commands like "mv foo FOO" to work as expected.
953 */
954 int search = old_parent_node != new_parent_node
955 || strcasecmp(old_name, new_name);
956 if (!(new_actual_name = find_file_within(new_parent_path, new_name,
957 new_child_path, sizeof(new_child_path), search))) {
958 res = -ENOENT;
959 goto io_error;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700960 }
961
Jeff Brown6249b902012-05-26 14:32:54 -0700962 TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path);
963 res = rename(old_child_path, new_child_path);
964 if (res < 0) {
965 res = -errno;
966 goto io_error;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700967 }
968
Jeff Brown6249b902012-05-26 14:32:54 -0700969 pthread_mutex_lock(&fuse->lock);
970 res = rename_node_locked(child_node, new_name, new_actual_name);
971 if (!res) {
972 remove_node_from_parent_locked(child_node);
973 add_node_to_parent_locked(child_node, new_parent_node);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700974 }
Jeff Brown6249b902012-05-26 14:32:54 -0700975 goto done;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700976
Jeff Brown6249b902012-05-26 14:32:54 -0700977io_error:
978 pthread_mutex_lock(&fuse->lock);
979done:
980 release_node_locked(child_node);
981lookup_error:
982 pthread_mutex_unlock(&fuse->lock);
983 return res;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700984}
985
Jeff Brown6249b902012-05-26 14:32:54 -0700986static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700987 const struct fuse_in_header* hdr, const struct fuse_open_in* req)
988{
Jeff Brown6249b902012-05-26 14:32:54 -0700989 struct node* node;
990 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700991 struct fuse_open_out out;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700992 struct handle *h;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700993
Jeff Brown6249b902012-05-26 14:32:54 -0700994 pthread_mutex_lock(&fuse->lock);
995 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
996 TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token,
997 req->flags, hdr->nodeid, node ? node->name : "?");
998 pthread_mutex_unlock(&fuse->lock);
999
1000 if (!node) {
1001 return -ENOENT;
1002 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001003 h = malloc(sizeof(*h));
1004 if (!h) {
Jeff Brown6249b902012-05-26 14:32:54 -07001005 return -ENOMEM;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001006 }
Jeff Brown6249b902012-05-26 14:32:54 -07001007 TRACE("[%d] OPEN %s\n", handler->token, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001008 h->fd = open(path, req->flags);
1009 if (h->fd < 0) {
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001010 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -07001011 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001012 }
1013 out.fh = ptr_to_id(h);
1014 out.open_flags = 0;
1015 out.padding = 0;
1016 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001017 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001018}
1019
Jeff Brown6249b902012-05-26 14:32:54 -07001020static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001021 const struct fuse_in_header* hdr, const struct fuse_read_in* req)
1022{
1023 struct handle *h = id_to_ptr(req->fh);
1024 __u64 unique = hdr->unique;
1025 __u32 size = req->size;
1026 __u64 offset = req->offset;
Jeff Brown6249b902012-05-26 14:32:54 -07001027 int res;
1028
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001029 /* Don't access any other fields of hdr or req beyond this point, the read buffer
1030 * overlaps the request buffer and will clobber data in the request. This
1031 * saves us 128KB per request handler thread at the cost of this scary comment. */
Jeff Brown6249b902012-05-26 14:32:54 -07001032
1033 TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token,
1034 h, h->fd, size, offset);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001035 if (size > sizeof(handler->read_buffer)) {
Jeff Brown6249b902012-05-26 14:32:54 -07001036 return -EINVAL;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001037 }
1038 res = pread64(h->fd, handler->read_buffer, size, offset);
1039 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -07001040 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001041 }
1042 fuse_reply(fuse, unique, handler->read_buffer, res);
Jeff Brown6249b902012-05-26 14:32:54 -07001043 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001044}
1045
Jeff Brown6249b902012-05-26 14:32:54 -07001046static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001047 const struct fuse_in_header* hdr, const struct fuse_write_in* req,
1048 const void* buffer)
1049{
1050 struct fuse_write_out out;
1051 struct handle *h = id_to_ptr(req->fh);
1052 int res;
Jeff Brown6249b902012-05-26 14:32:54 -07001053
1054 TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token,
1055 h, h->fd, req->size, req->offset);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001056 res = pwrite64(h->fd, buffer, req->size, req->offset);
1057 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -07001058 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001059 }
1060 out.size = res;
1061 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001062 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001063}
1064
Jeff Brown6249b902012-05-26 14:32:54 -07001065static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001066 const struct fuse_in_header* hdr)
1067{
Jeff Brown6249b902012-05-26 14:32:54 -07001068 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001069 struct statfs stat;
1070 struct fuse_statfs_out out;
1071 int res;
1072
Jeff Brown6249b902012-05-26 14:32:54 -07001073 pthread_mutex_lock(&fuse->lock);
1074 TRACE("[%d] STATFS\n", handler->token);
1075 res = get_node_path_locked(&fuse->root, path, sizeof(path));
1076 pthread_mutex_unlock(&fuse->lock);
1077 if (res < 0) {
1078 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001079 }
Jeff Brown6249b902012-05-26 14:32:54 -07001080 if (statfs(fuse->root.name, &stat) < 0) {
1081 return -errno;
1082 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001083 memset(&out, 0, sizeof(out));
1084 out.st.blocks = stat.f_blocks;
1085 out.st.bfree = stat.f_bfree;
1086 out.st.bavail = stat.f_bavail;
1087 out.st.files = stat.f_files;
1088 out.st.ffree = stat.f_ffree;
1089 out.st.bsize = stat.f_bsize;
1090 out.st.namelen = stat.f_namelen;
1091 out.st.frsize = stat.f_frsize;
1092 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001093 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001094}
1095
Jeff Brown6249b902012-05-26 14:32:54 -07001096static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001097 const struct fuse_in_header* hdr, const struct fuse_release_in* req)
1098{
1099 struct handle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -07001100
1101 TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001102 close(h->fd);
1103 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -07001104 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001105}
1106
Jeff Brown6249b902012-05-26 14:32:54 -07001107static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001108 const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
1109{
1110 int is_data_sync = req->fsync_flags & 1;
1111 struct handle *h = id_to_ptr(req->fh);
1112 int res;
Jeff Brown6249b902012-05-26 14:32:54 -07001113
1114 TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token,
1115 h, h->fd, is_data_sync);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001116 res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd);
1117 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -07001118 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001119 }
Jeff Brown6249b902012-05-26 14:32:54 -07001120 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001121}
1122
Jeff Brown6249b902012-05-26 14:32:54 -07001123static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001124 const struct fuse_in_header* hdr)
1125{
Jeff Brown6249b902012-05-26 14:32:54 -07001126 TRACE("[%d] FLUSH\n", handler->token);
1127 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001128}
1129
Jeff Brown6249b902012-05-26 14:32:54 -07001130static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001131 const struct fuse_in_header* hdr, const struct fuse_open_in* req)
1132{
Jeff Brown6249b902012-05-26 14:32:54 -07001133 struct node* node;
1134 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001135 struct fuse_open_out out;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001136 struct dirhandle *h;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001137
Jeff Brown6249b902012-05-26 14:32:54 -07001138 pthread_mutex_lock(&fuse->lock);
1139 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
1140 TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token,
1141 hdr->nodeid, node ? node->name : "?");
1142 pthread_mutex_unlock(&fuse->lock);
1143
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001144 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -07001145 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001146 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001147 h = malloc(sizeof(*h));
1148 if (!h) {
Jeff Brown6249b902012-05-26 14:32:54 -07001149 return -ENOMEM;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001150 }
Jeff Brown6249b902012-05-26 14:32:54 -07001151 TRACE("[%d] OPENDIR %s\n", handler->token, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001152 h->d = opendir(path);
Jeff Brown6249b902012-05-26 14:32:54 -07001153 if (!h->d) {
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001154 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -07001155 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001156 }
1157 out.fh = ptr_to_id(h);
1158 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001159 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001160}
1161
Jeff Brown6249b902012-05-26 14:32:54 -07001162static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001163 const struct fuse_in_header* hdr, const struct fuse_read_in* req)
1164{
1165 char buffer[8192];
1166 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
1167 struct dirent *de;
1168 struct dirhandle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -07001169
1170 TRACE("[%d] READDIR %p\n", handler->token, h);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001171 if (req->offset == 0) {
1172 /* rewinddir() might have been called above us, so rewind here too */
Jeff Brown6249b902012-05-26 14:32:54 -07001173 TRACE("[%d] calling rewinddir()\n", handler->token);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001174 rewinddir(h->d);
1175 }
1176 de = readdir(h->d);
1177 if (!de) {
Jeff Brown6249b902012-05-26 14:32:54 -07001178 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001179 }
1180 fde->ino = FUSE_UNKNOWN_INO;
1181 /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
1182 fde->off = req->offset + 1;
1183 fde->type = de->d_type;
1184 fde->namelen = strlen(de->d_name);
1185 memcpy(fde->name, de->d_name, fde->namelen + 1);
1186 fuse_reply(fuse, hdr->unique, fde,
Jeff Brown6249b902012-05-26 14:32:54 -07001187 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
1188 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001189}
1190
Jeff Brown6249b902012-05-26 14:32:54 -07001191static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001192 const struct fuse_in_header* hdr, const struct fuse_release_in* req)
1193{
1194 struct dirhandle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -07001195
1196 TRACE("[%d] RELEASEDIR %p\n", handler->token, h);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001197 closedir(h->d);
1198 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -07001199 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001200}
1201
Jeff Brown6249b902012-05-26 14:32:54 -07001202static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001203 const struct fuse_in_header* hdr, const struct fuse_init_in* req)
1204{
1205 struct fuse_init_out out;
1206
Jeff Brown6249b902012-05-26 14:32:54 -07001207 TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n",
1208 handler->token, req->major, req->minor, req->max_readahead, req->flags);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001209 out.major = FUSE_KERNEL_VERSION;
1210 out.minor = FUSE_KERNEL_MINOR_VERSION;
1211 out.max_readahead = req->max_readahead;
1212 out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
1213 out.max_background = 32;
1214 out.congestion_threshold = 32;
1215 out.max_write = MAX_WRITE;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001216 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001217 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001218}
1219
Jeff Brown6249b902012-05-26 14:32:54 -07001220static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001221 const struct fuse_in_header *hdr, const void *data, size_t data_len)
1222{
Brian Swetland03ee9472010-08-12 18:01:08 -07001223 switch (hdr->opcode) {
1224 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001225 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001226 return handle_lookup(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001227 }
Jeff Brown84715842012-05-25 14:07:47 -07001228
Brian Swetland03ee9472010-08-12 18:01:08 -07001229 case FUSE_FORGET: {
Jeff Brown84715842012-05-25 14:07:47 -07001230 const struct fuse_forget_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001231 return handle_forget(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001232 }
Jeff Brown84715842012-05-25 14:07:47 -07001233
Brian Swetland03ee9472010-08-12 18:01:08 -07001234 case FUSE_GETATTR: { /* getattr_in -> attr_out */
Jeff Brown84715842012-05-25 14:07:47 -07001235 const struct fuse_getattr_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001236 return handle_getattr(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001237 }
Jeff Brown84715842012-05-25 14:07:47 -07001238
Brian Swetland03ee9472010-08-12 18:01:08 -07001239 case FUSE_SETATTR: { /* setattr_in -> attr_out */
Jeff Brown84715842012-05-25 14:07:47 -07001240 const struct fuse_setattr_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001241 return handle_setattr(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001242 }
Jeff Brown84715842012-05-25 14:07:47 -07001243
Brian Swetland03ee9472010-08-12 18:01:08 -07001244// case FUSE_READLINK:
1245// case FUSE_SYMLINK:
1246 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001247 const struct fuse_mknod_in *req = data;
1248 const char *name = ((const char*) data) + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001249 return handle_mknod(fuse, handler, hdr, req, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001250 }
Jeff Brown84715842012-05-25 14:07:47 -07001251
Brian Swetland03ee9472010-08-12 18:01:08 -07001252 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001253 const struct fuse_mkdir_in *req = data;
1254 const char *name = ((const char*) data) + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001255 return handle_mkdir(fuse, handler, hdr, req, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001256 }
Jeff Brown84715842012-05-25 14:07:47 -07001257
Brian Swetland03ee9472010-08-12 18:01:08 -07001258 case FUSE_UNLINK: { /* bytez[] -> */
Jeff Brown84715842012-05-25 14:07:47 -07001259 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001260 return handle_unlink(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001261 }
Jeff Brown84715842012-05-25 14:07:47 -07001262
Brian Swetland03ee9472010-08-12 18:01:08 -07001263 case FUSE_RMDIR: { /* bytez[] -> */
Jeff Brown84715842012-05-25 14:07:47 -07001264 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001265 return handle_rmdir(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001266 }
Jeff Brown84715842012-05-25 14:07:47 -07001267
Brian Swetland03ee9472010-08-12 18:01:08 -07001268 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
Jeff Brown84715842012-05-25 14:07:47 -07001269 const struct fuse_rename_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001270 const char *old_name = ((const char*) data) + sizeof(*req);
1271 const char *new_name = old_name + strlen(old_name) + 1;
1272 return handle_rename(fuse, handler, hdr, req, old_name, new_name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001273 }
Jeff Brown84715842012-05-25 14:07:47 -07001274
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001275// case FUSE_LINK:
Brian Swetland03ee9472010-08-12 18:01:08 -07001276 case FUSE_OPEN: { /* open_in -> open_out */
Jeff Brown84715842012-05-25 14:07:47 -07001277 const struct fuse_open_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001278 return handle_open(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001279 }
Jeff Brown84715842012-05-25 14:07:47 -07001280
Brian Swetland03ee9472010-08-12 18:01:08 -07001281 case FUSE_READ: { /* read_in -> byte[] */
Jeff Brown84715842012-05-25 14:07:47 -07001282 const struct fuse_read_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001283 return handle_read(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001284 }
Jeff Brown84715842012-05-25 14:07:47 -07001285
Brian Swetland03ee9472010-08-12 18:01:08 -07001286 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
Jeff Brown84715842012-05-25 14:07:47 -07001287 const struct fuse_write_in *req = data;
1288 const void* buffer = (const __u8*)data + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001289 return handle_write(fuse, handler, hdr, req, buffer);
Brian Swetland03ee9472010-08-12 18:01:08 -07001290 }
Jeff Brown84715842012-05-25 14:07:47 -07001291
Mike Lockwood4553b082010-08-16 14:14:44 -04001292 case FUSE_STATFS: { /* getattr_in -> attr_out */
Jeff Brown6249b902012-05-26 14:32:54 -07001293 return handle_statfs(fuse, handler, hdr);
Mike Lockwood4553b082010-08-16 14:14:44 -04001294 }
Jeff Brown84715842012-05-25 14:07:47 -07001295
Brian Swetland03ee9472010-08-12 18:01:08 -07001296 case FUSE_RELEASE: { /* release_in -> */
Jeff Brown84715842012-05-25 14:07:47 -07001297 const struct fuse_release_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001298 return handle_release(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001299 }
Jeff Brown84715842012-05-25 14:07:47 -07001300
Jeff Brown6fd921a2012-05-25 15:01:21 -07001301 case FUSE_FSYNC: {
1302 const struct fuse_fsync_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001303 return handle_fsync(fuse, handler, hdr, req);
Jeff Brown6fd921a2012-05-25 15:01:21 -07001304 }
1305
Brian Swetland03ee9472010-08-12 18:01:08 -07001306// case FUSE_SETXATTR:
1307// case FUSE_GETXATTR:
1308// case FUSE_LISTXATTR:
1309// case FUSE_REMOVEXATTR:
Jeff Brown84715842012-05-25 14:07:47 -07001310 case FUSE_FLUSH: {
Jeff Brown6249b902012-05-26 14:32:54 -07001311 return handle_flush(fuse, handler, hdr);
Jeff Brown84715842012-05-25 14:07:47 -07001312 }
1313
Brian Swetland03ee9472010-08-12 18:01:08 -07001314 case FUSE_OPENDIR: { /* open_in -> open_out */
Jeff Brown84715842012-05-25 14:07:47 -07001315 const struct fuse_open_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001316 return handle_opendir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001317 }
Jeff Brown84715842012-05-25 14:07:47 -07001318
Brian Swetland03ee9472010-08-12 18:01:08 -07001319 case FUSE_READDIR: {
Jeff Brown84715842012-05-25 14:07:47 -07001320 const struct fuse_read_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001321 return handle_readdir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001322 }
Jeff Brown84715842012-05-25 14:07:47 -07001323
Brian Swetland03ee9472010-08-12 18:01:08 -07001324 case FUSE_RELEASEDIR: { /* release_in -> */
Jeff Brown84715842012-05-25 14:07:47 -07001325 const struct fuse_release_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001326 return handle_releasedir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001327 }
Jeff Brown84715842012-05-25 14:07:47 -07001328
Brian Swetland03ee9472010-08-12 18:01:08 -07001329// case FUSE_FSYNCDIR:
1330 case FUSE_INIT: { /* init_in -> init_out */
Jeff Brown84715842012-05-25 14:07:47 -07001331 const struct fuse_init_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001332 return handle_init(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001333 }
Jeff Brown84715842012-05-25 14:07:47 -07001334
Brian Swetland03ee9472010-08-12 18:01:08 -07001335 default: {
Jeff Brown6249b902012-05-26 14:32:54 -07001336 TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n",
1337 handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
1338 return -ENOSYS;
Brian Swetland03ee9472010-08-12 18:01:08 -07001339 }
Jeff Brown84715842012-05-25 14:07:47 -07001340 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001341}
1342
Jeff Brown6249b902012-05-26 14:32:54 -07001343static void handle_fuse_requests(struct fuse_handler* handler)
Brian Swetland03ee9472010-08-12 18:01:08 -07001344{
Jeff Brown6249b902012-05-26 14:32:54 -07001345 struct fuse* fuse = handler->fuse;
Brian Swetland03ee9472010-08-12 18:01:08 -07001346 for (;;) {
Jeff Brown6249b902012-05-26 14:32:54 -07001347 ssize_t len = read(fuse->fd,
1348 handler->request_buffer, sizeof(handler->request_buffer));
Brian Swetland03ee9472010-08-12 18:01:08 -07001349 if (len < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -07001350 if (errno != EINTR) {
1351 ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
1352 }
1353 continue;
Brian Swetland03ee9472010-08-12 18:01:08 -07001354 }
Jeff Brown84715842012-05-25 14:07:47 -07001355
1356 if ((size_t)len < sizeof(struct fuse_in_header)) {
Jeff Brown6249b902012-05-26 14:32:54 -07001357 ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len);
1358 continue;
Jeff Brown84715842012-05-25 14:07:47 -07001359 }
1360
Jeff Brown7729d242012-05-25 15:35:28 -07001361 const struct fuse_in_header *hdr = (void*)handler->request_buffer;
Jeff Brown84715842012-05-25 14:07:47 -07001362 if (hdr->len != (size_t)len) {
Jeff Brown6249b902012-05-26 14:32:54 -07001363 ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
1364 handler->token, (size_t)len, hdr->len);
1365 continue;
Jeff Brown84715842012-05-25 14:07:47 -07001366 }
1367
Jeff Brown7729d242012-05-25 15:35:28 -07001368 const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
Jeff Brown84715842012-05-25 14:07:47 -07001369 size_t data_len = len - sizeof(struct fuse_in_header);
Jeff Brown6249b902012-05-26 14:32:54 -07001370 __u64 unique = hdr->unique;
1371 int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
Jeff Brown7729d242012-05-25 15:35:28 -07001372
1373 /* We do not access the request again after this point because the underlying
1374 * buffer storage may have been reused while processing the request. */
Jeff Brown6249b902012-05-26 14:32:54 -07001375
1376 if (res != NO_STATUS) {
1377 if (res) {
1378 TRACE("[%d] ERROR %d\n", handler->token, res);
1379 }
1380 fuse_status(fuse, unique, res);
1381 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001382 }
1383}
1384
Jeff Brown6249b902012-05-26 14:32:54 -07001385static void* start_handler(void* data)
Jeff Brown7729d242012-05-25 15:35:28 -07001386{
Jeff Brown6249b902012-05-26 14:32:54 -07001387 struct fuse_handler* handler = data;
1388 handle_fuse_requests(handler);
1389 return NULL;
1390}
1391
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001392static bool hashmap_remove(void *key, void *value, void *context) {
1393 Hashmap* map = context;
1394 hashmapRemove(map, key);
1395 free(key);
1396 free(value);
1397 return true;
1398}
1399
1400static int read_package_list(struct fuse *fuse) {
1401 pthread_mutex_lock(&fuse->lock);
1402
1403 hashmapForEach(fuse->package_to_appid, hashmap_remove, fuse->package_to_appid);
1404
1405 FILE* file = fopen(kPackagesListFile, "r");
1406 if (!file) {
1407 ERROR("failed to open package list: %s\n", strerror(errno));
1408 pthread_mutex_unlock(&fuse->lock);
1409 return -1;
1410 }
1411
1412 char buf[512];
1413 while (fgets(buf, sizeof(buf), file) != NULL) {
1414 char package_name[512];
1415 int appid;
1416 if (sscanf(buf, "%s %d", package_name, &appid) == 2) {
1417 char* package_name_dup = strdup(package_name);
1418 struct package* package = malloc(sizeof(struct package));
1419 if (!package_name_dup || !package) {
1420 ERROR("cannot allocate package details\n");
1421 return -ENOMEM;
1422 }
1423
1424 package->appid = appid;
1425 hashmapPut(fuse->package_to_appid, package_name_dup, package);
1426 }
1427 }
1428
1429 TRACE("read_package_list: found %d packages\n", hashmapSize(fuse->package_to_appid));
1430 fclose(file);
1431 pthread_mutex_unlock(&fuse->lock);
1432 return 0;
1433}
1434
1435static void watch_package_list(struct fuse* fuse) {
1436 struct inotify_event *event;
1437 char event_buf[512];
1438
1439 int nfd = inotify_init();
1440 if (nfd < 0) {
1441 ERROR("inotify_init failed: %s\n", strerror(errno));
1442 return;
1443 }
1444
1445 bool active = false;
1446 while (1) {
1447 if (!active) {
1448 int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
1449 if (res == -1) {
1450 if (errno == ENOENT || errno == EACCES) {
1451 /* Framework may not have created yet, sleep and retry */
1452 ERROR("missing packages.list; retrying\n");
1453 sleep(3);
1454 continue;
1455 } else {
1456 ERROR("inotify_add_watch failed: %s\n", strerror(errno));
1457 return;
1458 }
1459 }
1460
1461 /* Watch above will tell us about any future changes, so
1462 * read the current state. */
1463 if (read_package_list(fuse) == -1) {
1464 ERROR("read_package_list failed: %s\n", strerror(errno));
1465 return;
1466 }
1467 active = true;
1468 }
1469
1470 int event_pos = 0;
1471 int res = read(nfd, event_buf, sizeof(event_buf));
1472 if (res < (int) sizeof(*event)) {
1473 if (errno == EINTR)
1474 continue;
1475 ERROR("failed to read inotify event: %s\n", strerror(errno));
1476 return;
1477 }
1478
1479 while (res >= (int) sizeof(*event)) {
1480 int event_size;
1481 event = (struct inotify_event *) (event_buf + event_pos);
1482
1483 TRACE("inotify event: %08x\n", event->mask);
1484 if ((event->mask & IN_IGNORED) == IN_IGNORED) {
1485 /* Previously watched file was deleted, probably due to move
1486 * that swapped in new data; re-arm the watch and read. */
1487 active = false;
1488 }
1489
1490 event_size = sizeof(*event) + event->len;
1491 res -= event_size;
1492 event_pos += event_size;
1493 }
1494 }
1495}
1496
Jeff Brown6249b902012-05-26 14:32:54 -07001497static int ignite_fuse(struct fuse* fuse, int num_threads)
1498{
1499 struct fuse_handler* handlers;
1500 int i;
1501
1502 handlers = malloc(num_threads * sizeof(struct fuse_handler));
1503 if (!handlers) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001504 ERROR("cannot allocate storage for threads\n");
Jeff Brown6249b902012-05-26 14:32:54 -07001505 return -ENOMEM;
1506 }
1507
1508 for (i = 0; i < num_threads; i++) {
1509 handlers[i].fuse = fuse;
1510 handlers[i].token = i;
1511 }
1512
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001513 /* When deriving permissions, this thread is used to process inotify events,
1514 * otherwise it becomes one of the FUSE handlers. */
1515 i = fuse->derive_perms ? 0 : 1;
1516 for (; i < num_threads; i++) {
Jeff Brown6249b902012-05-26 14:32:54 -07001517 pthread_t thread;
1518 int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
1519 if (res) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001520 ERROR("failed to start thread #%d, error=%d\n", i, res);
Jeff Brown6249b902012-05-26 14:32:54 -07001521 goto quit;
1522 }
1523 }
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001524
1525 if (fuse->derive_perms) {
1526 watch_package_list(fuse);
1527 } else {
1528 handle_fuse_requests(&handlers[0]);
1529 }
1530
1531 ERROR("terminated prematurely\n");
Jeff Brown6249b902012-05-26 14:32:54 -07001532
1533 /* don't bother killing all of the other threads or freeing anything,
1534 * should never get here anyhow */
1535quit:
1536 exit(1);
Jeff Brown7729d242012-05-25 15:35:28 -07001537}
1538
Mike Lockwood4f35e622011-01-12 14:39:44 -05001539static int usage()
1540{
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001541 ERROR("usage: sdcard [OPTIONS] <source_path> <dest_path>\n"
1542 " -u: specify UID to run as\n"
1543 " -g: specify GID to run as\n"
1544 " -t: specify number of threads to use (default %d)\n"
1545 " -d: derive file permissions based on path\n"
Jeff Brown6249b902012-05-26 14:32:54 -07001546 "\n", DEFAULT_NUM_THREADS);
Jeff Brown26567352012-05-25 13:27:43 -07001547 return 1;
1548}
1549
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001550static int run(const char* source_path, const char* dest_path, uid_t uid, gid_t gid,
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001551 int num_threads, bool derive_perms) {
Jeff Brown26567352012-05-25 13:27:43 -07001552 int fd;
1553 char opts[256];
1554 int res;
1555 struct fuse fuse;
1556
1557 /* cleanup from previous instance, if necessary */
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001558 umount2(dest_path, 2);
Jeff Brown26567352012-05-25 13:27:43 -07001559
1560 fd = open("/dev/fuse", O_RDWR);
1561 if (fd < 0){
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001562 ERROR("cannot open fuse device: %s\n", strerror(errno));
Jeff Brown26567352012-05-25 13:27:43 -07001563 return -1;
1564 }
1565
1566 snprintf(opts, sizeof(opts),
1567 "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
1568 fd, uid, gid);
1569
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001570 res = mount("/dev/fuse", dest_path, "fuse", MS_NOSUID | MS_NODEV, opts);
Jeff Brown26567352012-05-25 13:27:43 -07001571 if (res < 0) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001572 ERROR("cannot mount fuse filesystem: %s\n", strerror(errno));
1573 goto error;
1574 }
1575
1576 res = setgroups(sizeof(kGroups) / sizeof(kGroups[0]), kGroups);
1577 if (res < 0) {
1578 ERROR("cannot setgroups: %s\n", strerror(errno));
Jeff Brown26567352012-05-25 13:27:43 -07001579 goto error;
1580 }
1581
1582 res = setgid(gid);
1583 if (res < 0) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001584 ERROR("cannot setgid: %s\n", strerror(errno));
Jeff Brown26567352012-05-25 13:27:43 -07001585 goto error;
1586 }
1587
1588 res = setuid(uid);
1589 if (res < 0) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001590 ERROR("cannot setuid: %s\n", strerror(errno));
Jeff Brown26567352012-05-25 13:27:43 -07001591 goto error;
1592 }
1593
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001594 fuse_init(&fuse, fd, source_path, derive_perms);
Jeff Brown26567352012-05-25 13:27:43 -07001595
1596 umask(0);
Jeff Brown6249b902012-05-26 14:32:54 -07001597 res = ignite_fuse(&fuse, num_threads);
Jeff Brown26567352012-05-25 13:27:43 -07001598
1599 /* we do not attempt to umount the file system here because we are no longer
1600 * running as the root user */
Jeff Brown26567352012-05-25 13:27:43 -07001601
1602error:
1603 close(fd);
1604 return res;
Mike Lockwood4f35e622011-01-12 14:39:44 -05001605}
1606
Brian Swetland03ee9472010-08-12 18:01:08 -07001607int main(int argc, char **argv)
1608{
Brian Swetland03ee9472010-08-12 18:01:08 -07001609 int res;
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001610 const char *source_path = NULL;
1611 const char *dest_path = NULL;
Jeff Brown26567352012-05-25 13:27:43 -07001612 uid_t uid = 0;
1613 gid_t gid = 0;
Jeff Brown6249b902012-05-26 14:32:54 -07001614 int num_threads = DEFAULT_NUM_THREADS;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001615 bool derive_perms = false;
Mike Lockwood4f35e622011-01-12 14:39:44 -05001616 int i;
Ken Sumrall2fd72cc2013-02-08 16:50:55 -08001617 struct rlimit rlim;
Brian Swetland03ee9472010-08-12 18:01:08 -07001618
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001619 int opt;
1620 while ((opt = getopt(argc, argv, "u:g:t:d")) != -1) {
1621 switch (opt) {
1622 case 'u':
1623 uid = strtoul(optarg, NULL, 10);
1624 break;
1625 case 'g':
1626 gid = strtoul(optarg, NULL, 10);
1627 break;
1628 case 't':
1629 num_threads = strtoul(optarg, NULL, 10);
1630 break;
1631 case 'd':
1632 derive_perms = true;
1633 break;
1634 case '?':
1635 default:
1636 return usage();
1637 }
1638 }
1639
1640 for (i = optind; i < argc; i++) {
Mike Lockwood4f35e622011-01-12 14:39:44 -05001641 char* arg = argv[i];
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001642 if (!source_path) {
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001643 source_path = arg;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001644 } else if (!dest_path) {
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001645 dest_path = arg;
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001646 } else if (!uid) {
1647 uid = strtoul(arg, NULL, 10);
Jean-Baptiste Querue92372b2012-08-15 09:54:30 -07001648 } else if (!gid) {
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001649 gid = strtoul(arg, NULL, 10);
Jean-Baptiste Querue92372b2012-08-15 09:54:30 -07001650 } else {
Mike Lockwood575a2bb2011-01-23 14:46:30 -08001651 ERROR("too many arguments\n");
1652 return usage();
Mike Lockwood4f35e622011-01-12 14:39:44 -05001653 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001654 }
1655
Jeff Sharkeye169bd02012-08-13 16:44:42 -07001656 if (!source_path) {
1657 ERROR("no source path specified\n");
1658 return usage();
1659 }
1660 if (!dest_path) {
1661 ERROR("no dest path specified\n");
Mike Lockwood4f35e622011-01-12 14:39:44 -05001662 return usage();
1663 }
Jeff Brown26567352012-05-25 13:27:43 -07001664 if (!uid || !gid) {
Brian Swetland03ee9472010-08-12 18:01:08 -07001665 ERROR("uid and gid must be nonzero\n");
Mike Lockwood4f35e622011-01-12 14:39:44 -05001666 return usage();
Brian Swetland03ee9472010-08-12 18:01:08 -07001667 }
Jeff Brown6249b902012-05-26 14:32:54 -07001668 if (num_threads < 1) {
1669 ERROR("number of threads must be at least 1\n");
1670 return usage();
1671 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001672
Ken Sumrall2fd72cc2013-02-08 16:50:55 -08001673 rlim.rlim_cur = 8192;
1674 rlim.rlim_max = 8192;
1675 if (setrlimit(RLIMIT_NOFILE, &rlim)) {
1676 ERROR("Error setting RLIMIT_NOFILE, errno = %d\n", errno);
1677 }
1678
Jeff Sharkeydfe0cba2013-07-03 17:08:29 -07001679 res = run(source_path, dest_path, uid, gid, num_threads, derive_perms);
Jeff Brown26567352012-05-25 13:27:43 -07001680 return res < 0 ? 1 : 0;
Brian Swetland03ee9472010-08-12 18:01:08 -07001681}