blob: 316588c45b203e7f54f08c51ccf341e5e249c535 [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>
Brian Swetland03ee9472010-08-12 18:01:08 -070031
Brian Swetlandb14a2c62010-08-12 18:21:12 -070032#include <private/android_filesystem_config.h>
33
Brian Swetland03ee9472010-08-12 18:01:08 -070034#include "fuse.h"
35
36/* README
37 *
38 * What is this?
39 *
40 * sdcard is a program that uses FUSE to emulate FAT-on-sdcard style
41 * directory permissions (all files are given fixed owner, group, and
42 * permissions at creation, owner, group, and permissions are not
43 * changeable, symlinks and hardlinks are not createable, etc.
44 *
45 * usage: sdcard <path> <uid> <gid>
46 *
47 * It must be run as root, but will change to uid/gid as soon as it
Jeff Sharkeycfa9f652012-04-09 17:09:38 -070048 * mounts a filesystem on /storage/sdcard. It will refuse to run if uid or
Brian Swetland03ee9472010-08-12 18:01:08 -070049 * gid are zero.
50 *
51 *
52 * Things I believe to be true:
53 *
54 * - ops that return a fuse_entry (LOOKUP, MKNOD, MKDIR, LINK, SYMLINK,
55 * CREAT) must bump that node's refcount
56 * - don't forget that FORGET can forget multiple references (req->nlookup)
57 * - if an op that returns a fuse_entry fails writing the reply to the
58 * kernel, you must rollback the refcount to reflect the reference the
59 * kernel did not actually acquire
60 *
Brian Swetland03ee9472010-08-12 18:01:08 -070061 */
62
63#define FUSE_TRACE 0
64
65#if FUSE_TRACE
66#define TRACE(x...) fprintf(stderr,x)
67#else
68#define TRACE(x...) do {} while (0)
69#endif
70
71#define ERROR(x...) fprintf(stderr,x)
72
73#define FUSE_UNKNOWN_INO 0xffffffff
74
Jeff Sharkeycfa9f652012-04-09 17:09:38 -070075#define MOUNT_POINT "/storage/sdcard0"
Mike Lockwood4553b082010-08-16 14:14:44 -040076
Jeff Brown84715842012-05-25 14:07:47 -070077/* Maximum number of bytes to write in one request. */
78#define MAX_WRITE (256 * 1024)
79
80/* Maximum number of bytes to read in one request. */
81#define MAX_READ (128 * 1024)
82
83/* Largest possible request.
84 * The request size is bounded by the maximum size of a FUSE_WRITE request because it has
85 * the largest possible data payload. */
86#define MAX_REQUEST_SIZE (sizeof(struct fuse_in_header) + sizeof(struct fuse_write_in) + MAX_WRITE)
87
Jeff Brown6249b902012-05-26 14:32:54 -070088/* Default number of threads. */
89#define DEFAULT_NUM_THREADS 2
90
91/* Pseudo-error constant used to indicate that no fuse status is needed
92 * or that a reply has already been written. */
93#define NO_STATUS 1
94
Brian Swetland03ee9472010-08-12 18:01:08 -070095struct handle {
Brian Swetland03ee9472010-08-12 18:01:08 -070096 int fd;
97};
98
99struct dirhandle {
Brian Swetland03ee9472010-08-12 18:01:08 -0700100 DIR *d;
101};
102
103struct node {
Jeff Brown6249b902012-05-26 14:32:54 -0700104 __u32 refcount;
Brian Swetland03ee9472010-08-12 18:01:08 -0700105 __u64 nid;
106 __u64 gen;
107
Paul Eastham11ccdb32010-10-14 11:04:26 -0700108 struct node *next; /* per-dir sibling list */
109 struct node *child; /* first contained file by this dir */
Paul Eastham11ccdb32010-10-14 11:04:26 -0700110 struct node *parent; /* containing directory */
Brian Swetland03ee9472010-08-12 18:01:08 -0700111
Jeff Brown6249b902012-05-26 14:32:54 -0700112 size_t namelen;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700113 char *name;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800114 /* If non-null, this is the real name of the file in the underlying storage.
115 * This may differ from the field "name" only by case.
116 * strlen(actual_name) will always equal strlen(name), so it is safe to use
117 * namelen for both fields.
118 */
119 char *actual_name;
Brian Swetland03ee9472010-08-12 18:01:08 -0700120};
121
Jeff Brown7729d242012-05-25 15:35:28 -0700122/* Global data structure shared by all fuse handlers. */
Brian Swetland03ee9472010-08-12 18:01:08 -0700123struct fuse {
Jeff Brown6249b902012-05-26 14:32:54 -0700124 pthread_mutex_t lock;
125
Brian Swetland03ee9472010-08-12 18:01:08 -0700126 __u64 next_generation;
Brian Swetland03ee9472010-08-12 18:01:08 -0700127 int fd;
Brian Swetland03ee9472010-08-12 18:01:08 -0700128 struct node root;
Jeff Brown7729d242012-05-25 15:35:28 -0700129 char rootpath[PATH_MAX];
Brian Swetland03ee9472010-08-12 18:01:08 -0700130};
131
Jeff Brown7729d242012-05-25 15:35:28 -0700132/* Private data used by a single fuse handler. */
133struct fuse_handler {
Jeff Brown6249b902012-05-26 14:32:54 -0700134 struct fuse* fuse;
135 int token;
136
Jeff Brown7729d242012-05-25 15:35:28 -0700137 /* To save memory, we never use the contents of the request buffer and the read
138 * buffer at the same time. This allows us to share the underlying storage. */
139 union {
140 __u8 request_buffer[MAX_REQUEST_SIZE];
141 __u8 read_buffer[MAX_READ];
142 };
143};
Brian Swetland03ee9472010-08-12 18:01:08 -0700144
Jeff Brown6249b902012-05-26 14:32:54 -0700145static inline void *id_to_ptr(__u64 nid)
Brian Swetland03ee9472010-08-12 18:01:08 -0700146{
Jeff Brown6249b902012-05-26 14:32:54 -0700147 return (void *) (uintptr_t) nid;
148}
Brian Swetland03ee9472010-08-12 18:01:08 -0700149
Jeff Brown6249b902012-05-26 14:32:54 -0700150static inline __u64 ptr_to_id(void *ptr)
151{
152 return (__u64) (uintptr_t) ptr;
153}
Brian Swetland03ee9472010-08-12 18:01:08 -0700154
Jeff Brown6249b902012-05-26 14:32:54 -0700155static void acquire_node_locked(struct node* node)
156{
157 node->refcount++;
158 TRACE("ACQUIRE %p (%s) rc=%d\n", node, node->name, node->refcount);
159}
160
161static void remove_node_from_parent_locked(struct node* node);
162
163static void release_node_locked(struct node* node)
164{
165 TRACE("RELEASE %p (%s) rc=%d\n", node, node->name, node->refcount);
166 if (node->refcount > 0) {
167 node->refcount--;
168 if (!node->refcount) {
169 TRACE("DESTROY %p (%s)\n", node, node->name);
170 remove_node_from_parent_locked(node);
171
172 /* TODO: remove debugging - poison memory */
173 memset(node->name, 0xef, node->namelen);
174 free(node->name);
175 free(node->actual_name);
176 memset(node, 0xfc, sizeof(*node));
177 free(node);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800178 }
Jeff Brown6249b902012-05-26 14:32:54 -0700179 } else {
180 ERROR("Zero refcnt %p\n", node);
181 }
182}
183
184static void add_node_to_parent_locked(struct node *node, struct node *parent) {
185 node->parent = parent;
186 node->next = parent->child;
187 parent->child = node;
188 acquire_node_locked(parent);
189}
190
191static void remove_node_from_parent_locked(struct node* node)
192{
193 if (node->parent) {
194 if (node->parent->child == node) {
195 node->parent->child = node->parent->child->next;
196 } else {
197 struct node *node2;
198 node2 = node->parent->child;
199 while (node2->next != node)
200 node2 = node2->next;
201 node2->next = node->next;
202 }
203 release_node_locked(node->parent);
204 node->parent = NULL;
205 node->next = NULL;
206 }
207}
208
209/* Gets the absolute path to a node into the provided buffer.
210 *
211 * Populates 'buf' with the path and returns the length of the path on success,
212 * or returns -1 if the path is too long for the provided buffer.
213 */
214static ssize_t get_node_path_locked(struct node* node, char* buf, size_t bufsize)
215{
216 size_t namelen = node->namelen;
217 if (bufsize < namelen + 1) {
218 return -1;
Brian Swetland03ee9472010-08-12 18:01:08 -0700219 }
220
Jeff Brown6249b902012-05-26 14:32:54 -0700221 ssize_t pathlen = 0;
222 if (node->parent) {
223 pathlen = get_node_path_locked(node->parent, buf, bufsize - namelen - 2);
224 if (pathlen < 0) {
225 return -1;
226 }
227 buf[pathlen++] = '/';
228 }
229
230 const char* name = node->actual_name ? node->actual_name : node->name;
231 memcpy(buf + pathlen, name, namelen + 1); /* include trailing \0 */
232 return pathlen + namelen;
233}
234
235/* Finds the absolute path of a file within a given directory.
236 * Performs a case-insensitive search for the file and sets the buffer to the path
237 * of the first matching file. If 'search' is zero or if no match is found, sets
238 * the buffer to the path that the file would have, assuming the name were case-sensitive.
239 *
240 * Populates 'buf' with the path and returns the actual name (within 'buf') on success,
241 * or returns NULL if the path is too long for the provided buffer.
242 */
243static char* find_file_within(const char* path, const char* name,
244 char* buf, size_t bufsize, int search)
245{
246 size_t pathlen = strlen(path);
247 size_t namelen = strlen(name);
248 size_t childlen = pathlen + namelen + 1;
249 char* actual;
250
251 if (bufsize <= childlen) {
252 return NULL;
253 }
254
255 memcpy(buf, path, pathlen);
256 buf[pathlen] = '/';
257 actual = buf + pathlen + 1;
258 memcpy(actual, name, namelen + 1);
259
260 if (search && access(buf, F_OK)) {
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800261 struct dirent* entry;
Jeff Brown6249b902012-05-26 14:32:54 -0700262 DIR* dir = opendir(path);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800263 if (!dir) {
264 ERROR("opendir %s failed: %s", path, strerror(errno));
Jeff Brown6249b902012-05-26 14:32:54 -0700265 return actual;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800266 }
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800267 while ((entry = readdir(dir))) {
Jeff Brown6249b902012-05-26 14:32:54 -0700268 if (!strcasecmp(entry->d_name, name)) {
269 /* we have a match - replace the name, don't need to copy the null again */
270 memcpy(actual, entry->d_name, namelen);
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800271 break;
272 }
273 }
274 closedir(dir);
275 }
Jeff Brown6249b902012-05-26 14:32:54 -0700276 return actual;
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800277}
278
Jeff Brown6249b902012-05-26 14:32:54 -0700279static void attr_from_stat(struct fuse_attr *attr, const struct stat *s, __u64 nid)
Mike Lockwood575a2bb2011-01-23 14:46:30 -0800280{
Jeff Brown6249b902012-05-26 14:32:54 -0700281 attr->ino = nid;
Brian Swetland03ee9472010-08-12 18:01:08 -0700282 attr->size = s->st_size;
283 attr->blocks = s->st_blocks;
Mike Lockwood4553b082010-08-16 14:14:44 -0400284 attr->atime = s->st_atime;
285 attr->mtime = s->st_mtime;
286 attr->ctime = s->st_ctime;
287 attr->atimensec = s->st_atime_nsec;
288 attr->mtimensec = s->st_mtime_nsec;
289 attr->ctimensec = s->st_ctime_nsec;
Brian Swetland03ee9472010-08-12 18:01:08 -0700290 attr->mode = s->st_mode;
291 attr->nlink = s->st_nlink;
Brian Swetland03ee9472010-08-12 18:01:08 -0700292
Brian Swetlandb14a2c62010-08-12 18:21:12 -0700293 /* force permissions to something reasonable:
294 * world readable
295 * writable by the sdcard group
296 */
297 if (attr->mode & 0100) {
298 attr->mode = (attr->mode & (~0777)) | 0775;
299 } else {
300 attr->mode = (attr->mode & (~0777)) | 0664;
301 }
302
303 /* all files owned by root.sdcard */
304 attr->uid = 0;
305 attr->gid = AID_SDCARD_RW;
Brian Swetland03ee9472010-08-12 18:01:08 -0700306}
307
Jeff Brown6249b902012-05-26 14:32:54 -0700308struct node *create_node_locked(struct fuse* fuse,
309 struct node *parent, const char *name, const char* actual_name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700310{
311 struct node *node;
Jeff Brown6249b902012-05-26 14:32:54 -0700312 size_t namelen = strlen(name);
Brian Swetland03ee9472010-08-12 18:01:08 -0700313
Paul Eastham11ccdb32010-10-14 11:04:26 -0700314 node = calloc(1, sizeof(struct node));
Jeff Brown6249b902012-05-26 14:32:54 -0700315 if (!node) {
316 return NULL;
Brian Swetland03ee9472010-08-12 18:01:08 -0700317 }
Paul Eastham11ccdb32010-10-14 11:04:26 -0700318 node->name = malloc(namelen + 1);
Jeff Brown6249b902012-05-26 14:32:54 -0700319 if (!node->name) {
Paul Eastham11ccdb32010-10-14 11:04:26 -0700320 free(node);
Jeff Brown6249b902012-05-26 14:32:54 -0700321 return NULL;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700322 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700323 memcpy(node->name, name, namelen + 1);
Jeff Brown6249b902012-05-26 14:32:54 -0700324 if (strcmp(name, actual_name)) {
325 node->actual_name = malloc(namelen + 1);
326 if (!node->actual_name) {
327 free(node->name);
328 free(node);
329 return NULL;
330 }
331 memcpy(node->actual_name, actual_name, namelen + 1);
332 }
Brian Swetland03ee9472010-08-12 18:01:08 -0700333 node->namelen = namelen;
Jeff Brown6249b902012-05-26 14:32:54 -0700334 node->nid = ptr_to_id(node);
335 node->gen = fuse->next_generation++;
336 acquire_node_locked(node);
337 add_node_to_parent_locked(node, parent);
Brian Swetland03ee9472010-08-12 18:01:08 -0700338 return node;
339}
340
Jeff Brown6249b902012-05-26 14:32:54 -0700341static int rename_node_locked(struct node *node, const char *name,
342 const char* actual_name)
Paul Eastham11ccdb32010-10-14 11:04:26 -0700343{
Jeff Brown6249b902012-05-26 14:32:54 -0700344 size_t namelen = strlen(name);
345 int need_actual_name = strcmp(name, actual_name);
346
347 /* make the storage bigger without actually changing the name
348 * in case an error occurs part way */
349 if (namelen > node->namelen) {
350 char* new_name = realloc(node->name, namelen + 1);
351 if (!new_name) {
352 return -ENOMEM;
353 }
354 node->name = new_name;
355 if (need_actual_name && node->actual_name) {
356 char* new_actual_name = realloc(node->actual_name, namelen + 1);
357 if (!new_actual_name) {
358 return -ENOMEM;
359 }
360 node->actual_name = new_actual_name;
361 }
362 }
363
364 /* update the name, taking care to allocate storage before overwriting the old name */
365 if (need_actual_name) {
366 if (!node->actual_name) {
367 node->actual_name = malloc(namelen + 1);
368 if (!node->actual_name) {
369 return -ENOMEM;
370 }
371 }
372 memcpy(node->actual_name, actual_name, namelen + 1);
373 } else {
374 free(node->actual_name);
375 node->actual_name = NULL;
376 }
377 memcpy(node->name, name, namelen + 1);
378 node->namelen = namelen;
379 return 0;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700380}
381
Jeff Brown6249b902012-05-26 14:32:54 -0700382static struct node *lookup_node_by_id_locked(struct fuse *fuse, __u64 nid)
Brian Swetland03ee9472010-08-12 18:01:08 -0700383{
Jeff Brown6249b902012-05-26 14:32:54 -0700384 if (nid == FUSE_ROOT_ID) {
Brian Swetland03ee9472010-08-12 18:01:08 -0700385 return &fuse->root;
386 } else {
387 return id_to_ptr(nid);
388 }
389}
390
Jeff Brown6249b902012-05-26 14:32:54 -0700391static struct node* lookup_node_and_path_by_id_locked(struct fuse* fuse, __u64 nid,
392 char* buf, size_t bufsize)
393{
394 struct node* node = lookup_node_by_id_locked(fuse, nid);
395 if (node && get_node_path_locked(node, buf, bufsize) < 0) {
396 node = NULL;
397 }
398 return node;
399}
400
401static struct node *lookup_child_by_name_locked(struct node *node, const char *name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700402{
403 for (node = node->child; node; node = node->next) {
Jeff Brown6249b902012-05-26 14:32:54 -0700404 /* use exact string comparison, nodes that differ by case
405 * must be considered distinct even if they refer to the same
406 * underlying file as otherwise operations such as "mv x x"
407 * will not work because the source and target nodes are the same. */
Brian Swetland03ee9472010-08-12 18:01:08 -0700408 if (!strcmp(name, node->name)) {
409 return node;
410 }
411 }
412 return 0;
413}
414
Jeff Brown6249b902012-05-26 14:32:54 -0700415static struct node* acquire_or_create_child_locked(
416 struct fuse* fuse, struct node* parent,
417 const char* name, const char* actual_name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700418{
Jeff Brown6249b902012-05-26 14:32:54 -0700419 struct node* child = lookup_child_by_name_locked(parent, name);
420 if (child) {
421 acquire_node_locked(child);
Paul Eastham77085c52011-01-04 21:06:03 -0800422 } else {
Jeff Brown6249b902012-05-26 14:32:54 -0700423 child = create_node_locked(fuse, parent, name, actual_name);
Paul Eastham77085c52011-01-04 21:06:03 -0800424 }
Jeff Brown6249b902012-05-26 14:32:54 -0700425 return child;
Paul Eastham11ccdb32010-10-14 11:04:26 -0700426}
427
Jeff Brown6249b902012-05-26 14:32:54 -0700428static void fuse_init(struct fuse *fuse, int fd, const char *path)
Brian Swetland03ee9472010-08-12 18:01:08 -0700429{
Jeff Brown6249b902012-05-26 14:32:54 -0700430 pthread_mutex_init(&fuse->lock, NULL);
Brian Swetland03ee9472010-08-12 18:01:08 -0700431
Jeff Brown6249b902012-05-26 14:32:54 -0700432 fuse->fd = fd;
433 fuse->next_generation = 0;
Brian Swetland03ee9472010-08-12 18:01:08 -0700434
Jeff Brown6249b902012-05-26 14:32:54 -0700435 memset(&fuse->root, 0, sizeof(fuse->root));
436 fuse->root.nid = FUSE_ROOT_ID; /* 1 */
437 fuse->root.refcount = 2;
438 fuse->root.namelen = strlen(path);
439 fuse->root.name = strdup(path);
Brian Swetland03ee9472010-08-12 18:01:08 -0700440}
441
Jeff Brown6249b902012-05-26 14:32:54 -0700442static void fuse_status(struct fuse *fuse, __u64 unique, int err)
Brian Swetland03ee9472010-08-12 18:01:08 -0700443{
444 struct fuse_out_header hdr;
445 hdr.len = sizeof(hdr);
446 hdr.error = err;
447 hdr.unique = unique;
Brian Swetland03ee9472010-08-12 18:01:08 -0700448 write(fuse->fd, &hdr, sizeof(hdr));
449}
450
Jeff Brown6249b902012-05-26 14:32:54 -0700451static void fuse_reply(struct fuse *fuse, __u64 unique, void *data, int len)
Brian Swetland03ee9472010-08-12 18:01:08 -0700452{
453 struct fuse_out_header hdr;
454 struct iovec vec[2];
455 int res;
456
457 hdr.len = len + sizeof(hdr);
458 hdr.error = 0;
459 hdr.unique = unique;
460
461 vec[0].iov_base = &hdr;
462 vec[0].iov_len = sizeof(hdr);
463 vec[1].iov_base = data;
464 vec[1].iov_len = len;
465
466 res = writev(fuse->fd, vec, 2);
467 if (res < 0) {
468 ERROR("*** REPLY FAILED *** %d\n", errno);
469 }
470}
471
Jeff Brown6249b902012-05-26 14:32:54 -0700472static int fuse_reply_entry(struct fuse* fuse, __u64 unique,
473 struct node* parent, const char* name, const char* actual_name,
474 const char* path)
Brian Swetland03ee9472010-08-12 18:01:08 -0700475{
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700476 struct node* node;
Jeff Brown6249b902012-05-26 14:32:54 -0700477 struct fuse_entry_out out;
478 struct stat s;
Brian Swetland03ee9472010-08-12 18:01:08 -0700479
Jeff Brown6249b902012-05-26 14:32:54 -0700480 if (lstat(path, &s) < 0) {
481 return -errno;
Brian Swetland03ee9472010-08-12 18:01:08 -0700482 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700483
Jeff Brown6249b902012-05-26 14:32:54 -0700484 pthread_mutex_lock(&fuse->lock);
485 node = acquire_or_create_child_locked(fuse, parent, name, actual_name);
486 if (!node) {
487 pthread_mutex_unlock(&fuse->lock);
488 return -ENOMEM;
489 }
490 memset(&out, 0, sizeof(out));
491 attr_from_stat(&out.attr, &s, node->nid);
492 out.attr_valid = 10;
493 out.entry_valid = 10;
Brian Swetland03ee9472010-08-12 18:01:08 -0700494 out.nodeid = node->nid;
495 out.generation = node->gen;
Jeff Brown6249b902012-05-26 14:32:54 -0700496 pthread_mutex_unlock(&fuse->lock);
Brian Swetland03ee9472010-08-12 18:01:08 -0700497 fuse_reply(fuse, unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700498 return NO_STATUS;
Brian Swetland03ee9472010-08-12 18:01:08 -0700499}
500
Jeff Brown6249b902012-05-26 14:32:54 -0700501static int fuse_reply_attr(struct fuse* fuse, __u64 unique, __u64 nid,
502 const char* path)
503{
504 struct fuse_attr_out out;
505 struct stat s;
506
507 if (lstat(path, &s) < 0) {
508 return -errno;
509 }
510 memset(&out, 0, sizeof(out));
511 attr_from_stat(&out.attr, &s, nid);
512 out.attr_valid = 10;
513 fuse_reply(fuse, unique, &out, sizeof(out));
514 return NO_STATUS;
515}
516
517static int handle_lookup(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700518 const struct fuse_in_header *hdr, const char* name)
Brian Swetland03ee9472010-08-12 18:01:08 -0700519{
Jeff Brown6249b902012-05-26 14:32:54 -0700520 struct node* parent_node;
521 char parent_path[PATH_MAX];
522 char child_path[PATH_MAX];
523 const char* actual_name;
Brian Swetland03ee9472010-08-12 18:01:08 -0700524
Jeff Brown6249b902012-05-26 14:32:54 -0700525 pthread_mutex_lock(&fuse->lock);
526 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
527 parent_path, sizeof(parent_path));
528 TRACE("[%d] LOOKUP %s @ %llx (%s)\n", handler->token, name, hdr->nodeid,
529 parent_node ? parent_node->name : "?");
530 pthread_mutex_unlock(&fuse->lock);
531
532 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
533 child_path, sizeof(child_path), 1))) {
534 return -ENOENT;
Brian Swetland03ee9472010-08-12 18:01:08 -0700535 }
Jeff Brown6249b902012-05-26 14:32:54 -0700536 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700537}
538
Jeff Brown6249b902012-05-26 14:32:54 -0700539static int handle_forget(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700540 const struct fuse_in_header *hdr, const struct fuse_forget_in *req)
541{
Jeff Brown6249b902012-05-26 14:32:54 -0700542 struct node* node;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700543
Jeff Brown6249b902012-05-26 14:32:54 -0700544 pthread_mutex_lock(&fuse->lock);
545 node = lookup_node_by_id_locked(fuse, hdr->nodeid);
546 TRACE("[%d] FORGET #%lld @ %llx (%s)\n", handler->token, req->nlookup,
547 hdr->nodeid, node ? node->name : "?");
548 if (node) {
549 __u64 n = req->nlookup;
550 while (n--) {
551 release_node_locked(node);
552 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700553 }
Jeff Brown6249b902012-05-26 14:32:54 -0700554 pthread_mutex_unlock(&fuse->lock);
555 return NO_STATUS; /* no reply */
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700556}
557
Jeff Brown6249b902012-05-26 14:32:54 -0700558static int handle_getattr(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700559 const struct fuse_in_header *hdr, const struct fuse_getattr_in *req)
560{
Jeff Brown6249b902012-05-26 14:32:54 -0700561 struct node* node;
562 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700563
Jeff Brown6249b902012-05-26 14:32:54 -0700564 pthread_mutex_lock(&fuse->lock);
565 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
566 TRACE("[%d] GETATTR flags=%x fh=%llx @ %llx (%s)\n", handler->token,
567 req->getattr_flags, req->fh, hdr->nodeid, node ? node->name : "?");
568 pthread_mutex_unlock(&fuse->lock);
569
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700570 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -0700571 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700572 }
Jeff Brown6249b902012-05-26 14:32:54 -0700573 return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700574}
575
Jeff Brown6249b902012-05-26 14:32:54 -0700576static int handle_setattr(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700577 const struct fuse_in_header *hdr, const struct fuse_setattr_in *req)
578{
Jeff Brown6249b902012-05-26 14:32:54 -0700579 struct node* node;
580 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700581 struct timespec times[2];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700582
Jeff Brown6249b902012-05-26 14:32:54 -0700583 pthread_mutex_lock(&fuse->lock);
584 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
585 TRACE("[%d] SETATTR fh=%llx valid=%x @ %llx (%s)\n", handler->token,
586 req->fh, req->valid, hdr->nodeid, node ? node->name : "?");
587 pthread_mutex_unlock(&fuse->lock);
588
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700589 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -0700590 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700591 }
592
Jeff Brown6249b902012-05-26 14:32:54 -0700593 /* XXX: incomplete implementation on purpose.
594 * chmod/chown should NEVER be implemented.*/
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700595
Jeff Brown6249b902012-05-26 14:32:54 -0700596 if ((req->valid & FATTR_SIZE) && truncate(path, req->size) < 0) {
597 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700598 }
599
600 /* Handle changing atime and mtime. If FATTR_ATIME_and FATTR_ATIME_NOW
601 * are both set, then set it to the current time. Else, set it to the
602 * time specified in the request. Same goes for mtime. Use utimensat(2)
603 * as it allows ATIME and MTIME to be changed independently, and has
604 * nanosecond resolution which fuse also has.
605 */
606 if (req->valid & (FATTR_ATIME | FATTR_MTIME)) {
607 times[0].tv_nsec = UTIME_OMIT;
608 times[1].tv_nsec = UTIME_OMIT;
609 if (req->valid & FATTR_ATIME) {
610 if (req->valid & FATTR_ATIME_NOW) {
611 times[0].tv_nsec = UTIME_NOW;
612 } else {
613 times[0].tv_sec = req->atime;
614 times[0].tv_nsec = req->atimensec;
615 }
616 }
617 if (req->valid & FATTR_MTIME) {
618 if (req->valid & FATTR_MTIME_NOW) {
619 times[1].tv_nsec = UTIME_NOW;
620 } else {
621 times[1].tv_sec = req->mtime;
622 times[1].tv_nsec = req->mtimensec;
623 }
624 }
Jeff Brown6249b902012-05-26 14:32:54 -0700625 TRACE("[%d] Calling utimensat on %s with atime %ld, mtime=%ld\n",
626 handler->token, path, times[0].tv_sec, times[1].tv_sec);
627 if (utimensat(-1, path, times, 0) < 0) {
628 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700629 }
630 }
Jeff Brown6249b902012-05-26 14:32:54 -0700631 return fuse_reply_attr(fuse, hdr->unique, hdr->nodeid, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700632}
633
Jeff Brown6249b902012-05-26 14:32:54 -0700634static int handle_mknod(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700635 const struct fuse_in_header* hdr, const struct fuse_mknod_in* req, const char* name)
636{
Jeff Brown6249b902012-05-26 14:32:54 -0700637 struct node* parent_node;
638 char parent_path[PATH_MAX];
639 char child_path[PATH_MAX];
640 const char* actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700641
Jeff Brown6249b902012-05-26 14:32:54 -0700642 pthread_mutex_lock(&fuse->lock);
643 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
644 parent_path, sizeof(parent_path));
645 TRACE("[%d] MKNOD %s 0%o @ %llx (%s)\n", handler->token,
646 name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
647 pthread_mutex_unlock(&fuse->lock);
648
649 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
650 child_path, sizeof(child_path), 1))) {
651 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700652 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700653 __u32 mode = (req->mode & (~0777)) | 0664;
Jeff Brown6249b902012-05-26 14:32:54 -0700654 if (mknod(child_path, mode, req->rdev) < 0) {
655 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700656 }
Jeff Brown6249b902012-05-26 14:32:54 -0700657 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700658}
659
Jeff Brown6249b902012-05-26 14:32:54 -0700660static int handle_mkdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700661 const struct fuse_in_header* hdr, const struct fuse_mkdir_in* req, const char* name)
662{
Jeff Brown6249b902012-05-26 14:32:54 -0700663 struct node* parent_node;
664 char parent_path[PATH_MAX];
665 char child_path[PATH_MAX];
666 const char* actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700667
Jeff Brown6249b902012-05-26 14:32:54 -0700668 pthread_mutex_lock(&fuse->lock);
669 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
670 parent_path, sizeof(parent_path));
671 TRACE("[%d] MKDIR %s 0%o @ %llx (%s)\n", handler->token,
672 name, req->mode, hdr->nodeid, parent_node ? parent_node->name : "?");
673 pthread_mutex_unlock(&fuse->lock);
674
675 if (!parent_node || !(actual_name = find_file_within(parent_path, name,
676 child_path, sizeof(child_path), 1))) {
677 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700678 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700679 __u32 mode = (req->mode & (~0777)) | 0775;
Jeff Brown6249b902012-05-26 14:32:54 -0700680 if (mkdir(child_path, mode) < 0) {
681 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700682 }
Jeff Brown6249b902012-05-26 14:32:54 -0700683 return fuse_reply_entry(fuse, hdr->unique, parent_node, name, actual_name, child_path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700684}
685
Jeff Brown6249b902012-05-26 14:32:54 -0700686static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700687 const struct fuse_in_header* hdr, const char* name)
688{
Jeff Brown6249b902012-05-26 14:32:54 -0700689 struct node* parent_node;
690 char parent_path[PATH_MAX];
691 char child_path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700692
Jeff Brown6249b902012-05-26 14:32:54 -0700693 pthread_mutex_lock(&fuse->lock);
694 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
695 parent_path, sizeof(parent_path));
696 TRACE("[%d] UNLINK %s @ %llx (%s)\n", handler->token,
697 name, hdr->nodeid, parent_node ? parent_node->name : "?");
698 pthread_mutex_unlock(&fuse->lock);
699
700 if (!parent_node || !find_file_within(parent_path, name,
701 child_path, sizeof(child_path), 1)) {
702 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700703 }
Jeff Brown6249b902012-05-26 14:32:54 -0700704 if (unlink(child_path) < 0) {
705 return -errno;
706 }
707 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700708}
709
Jeff Brown6249b902012-05-26 14:32:54 -0700710static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700711 const struct fuse_in_header* hdr, const char* name)
712{
Jeff Brown6249b902012-05-26 14:32:54 -0700713 struct node* parent_node;
714 char parent_path[PATH_MAX];
715 char child_path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700716
Jeff Brown6249b902012-05-26 14:32:54 -0700717 pthread_mutex_lock(&fuse->lock);
718 parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
719 parent_path, sizeof(parent_path));
720 TRACE("[%d] RMDIR %s @ %llx (%s)\n", handler->token,
721 name, hdr->nodeid, parent_node ? parent_node->name : "?");
722 pthread_mutex_unlock(&fuse->lock);
723
724 if (!parent_node || !find_file_within(parent_path, name,
725 child_path, sizeof(child_path), 1)) {
726 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700727 }
Jeff Brown6249b902012-05-26 14:32:54 -0700728 if (rmdir(child_path) < 0) {
729 return -errno;
730 }
731 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700732}
733
Jeff Brown6249b902012-05-26 14:32:54 -0700734static int handle_rename(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700735 const struct fuse_in_header* hdr, const struct fuse_rename_in* req,
Jeff Brown6249b902012-05-26 14:32:54 -0700736 const char* old_name, const char* new_name)
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700737{
Jeff Brown6249b902012-05-26 14:32:54 -0700738 struct node* old_parent_node;
739 struct node* new_parent_node;
740 struct node* child_node;
741 char old_parent_path[PATH_MAX];
742 char new_parent_path[PATH_MAX];
743 char old_child_path[PATH_MAX];
744 char new_child_path[PATH_MAX];
745 const char* new_actual_name;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700746 int res;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700747
Jeff Brown6249b902012-05-26 14:32:54 -0700748 pthread_mutex_lock(&fuse->lock);
749 old_parent_node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid,
750 old_parent_path, sizeof(old_parent_path));
751 new_parent_node = lookup_node_and_path_by_id_locked(fuse, req->newdir,
752 new_parent_path, sizeof(new_parent_path));
753 TRACE("[%d] RENAME %s->%s @ %llx (%s) -> %llx (%s)\n", handler->token,
754 old_name, new_name,
755 hdr->nodeid, old_parent_node ? old_parent_node->name : "?",
756 req->newdir, new_parent_node ? new_parent_node->name : "?");
757 if (!old_parent_node || !new_parent_node) {
758 res = -ENOENT;
759 goto lookup_error;
760 }
761 child_node = lookup_child_by_name_locked(old_parent_node, old_name);
762 if (!child_node || get_node_path_locked(child_node,
763 old_child_path, sizeof(old_child_path)) < 0) {
764 res = -ENOENT;
765 goto lookup_error;
766 }
767 acquire_node_locked(child_node);
768 pthread_mutex_unlock(&fuse->lock);
769
770 /* Special case for renaming a file where destination is same path
771 * differing only by case. In this case we don't want to look for a case
772 * insensitive match. This allows commands like "mv foo FOO" to work as expected.
773 */
774 int search = old_parent_node != new_parent_node
775 || strcasecmp(old_name, new_name);
776 if (!(new_actual_name = find_file_within(new_parent_path, new_name,
777 new_child_path, sizeof(new_child_path), search))) {
778 res = -ENOENT;
779 goto io_error;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700780 }
781
Jeff Brown6249b902012-05-26 14:32:54 -0700782 TRACE("[%d] RENAME %s->%s\n", handler->token, old_child_path, new_child_path);
783 res = rename(old_child_path, new_child_path);
784 if (res < 0) {
785 res = -errno;
786 goto io_error;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700787 }
788
Jeff Brown6249b902012-05-26 14:32:54 -0700789 pthread_mutex_lock(&fuse->lock);
790 res = rename_node_locked(child_node, new_name, new_actual_name);
791 if (!res) {
792 remove_node_from_parent_locked(child_node);
793 add_node_to_parent_locked(child_node, new_parent_node);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700794 }
Jeff Brown6249b902012-05-26 14:32:54 -0700795 goto done;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700796
Jeff Brown6249b902012-05-26 14:32:54 -0700797io_error:
798 pthread_mutex_lock(&fuse->lock);
799done:
800 release_node_locked(child_node);
801lookup_error:
802 pthread_mutex_unlock(&fuse->lock);
803 return res;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700804}
805
Jeff Brown6249b902012-05-26 14:32:54 -0700806static int handle_open(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700807 const struct fuse_in_header* hdr, const struct fuse_open_in* req)
808{
Jeff Brown6249b902012-05-26 14:32:54 -0700809 struct node* node;
810 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700811 struct fuse_open_out out;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700812 struct handle *h;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700813
Jeff Brown6249b902012-05-26 14:32:54 -0700814 pthread_mutex_lock(&fuse->lock);
815 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
816 TRACE("[%d] OPEN 0%o @ %llx (%s)\n", handler->token,
817 req->flags, hdr->nodeid, node ? node->name : "?");
818 pthread_mutex_unlock(&fuse->lock);
819
820 if (!node) {
821 return -ENOENT;
822 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700823 h = malloc(sizeof(*h));
824 if (!h) {
Jeff Brown6249b902012-05-26 14:32:54 -0700825 return -ENOMEM;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700826 }
Jeff Brown6249b902012-05-26 14:32:54 -0700827 TRACE("[%d] OPEN %s\n", handler->token, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700828 h->fd = open(path, req->flags);
829 if (h->fd < 0) {
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700830 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -0700831 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700832 }
833 out.fh = ptr_to_id(h);
834 out.open_flags = 0;
835 out.padding = 0;
836 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700837 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700838}
839
Jeff Brown6249b902012-05-26 14:32:54 -0700840static int handle_read(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700841 const struct fuse_in_header* hdr, const struct fuse_read_in* req)
842{
843 struct handle *h = id_to_ptr(req->fh);
844 __u64 unique = hdr->unique;
845 __u32 size = req->size;
846 __u64 offset = req->offset;
Jeff Brown6249b902012-05-26 14:32:54 -0700847 int res;
848
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700849 /* Don't access any other fields of hdr or req beyond this point, the read buffer
850 * overlaps the request buffer and will clobber data in the request. This
851 * saves us 128KB per request handler thread at the cost of this scary comment. */
Jeff Brown6249b902012-05-26 14:32:54 -0700852
853 TRACE("[%d] READ %p(%d) %u@%llu\n", handler->token,
854 h, h->fd, size, offset);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700855 if (size > sizeof(handler->read_buffer)) {
Jeff Brown6249b902012-05-26 14:32:54 -0700856 return -EINVAL;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700857 }
858 res = pread64(h->fd, handler->read_buffer, size, offset);
859 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -0700860 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700861 }
862 fuse_reply(fuse, unique, handler->read_buffer, res);
Jeff Brown6249b902012-05-26 14:32:54 -0700863 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700864}
865
Jeff Brown6249b902012-05-26 14:32:54 -0700866static int handle_write(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700867 const struct fuse_in_header* hdr, const struct fuse_write_in* req,
868 const void* buffer)
869{
870 struct fuse_write_out out;
871 struct handle *h = id_to_ptr(req->fh);
872 int res;
Jeff Brown6249b902012-05-26 14:32:54 -0700873
874 TRACE("[%d] WRITE %p(%d) %u@%llu\n", handler->token,
875 h, h->fd, req->size, req->offset);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700876 res = pwrite64(h->fd, buffer, req->size, req->offset);
877 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -0700878 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700879 }
880 out.size = res;
881 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700882 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700883}
884
Jeff Brown6249b902012-05-26 14:32:54 -0700885static int handle_statfs(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700886 const struct fuse_in_header* hdr)
887{
Jeff Brown6249b902012-05-26 14:32:54 -0700888 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700889 struct statfs stat;
890 struct fuse_statfs_out out;
891 int res;
892
Jeff Brown6249b902012-05-26 14:32:54 -0700893 pthread_mutex_lock(&fuse->lock);
894 TRACE("[%d] STATFS\n", handler->token);
895 res = get_node_path_locked(&fuse->root, path, sizeof(path));
896 pthread_mutex_unlock(&fuse->lock);
897 if (res < 0) {
898 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700899 }
Jeff Brown6249b902012-05-26 14:32:54 -0700900 if (statfs(fuse->root.name, &stat) < 0) {
901 return -errno;
902 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700903 memset(&out, 0, sizeof(out));
904 out.st.blocks = stat.f_blocks;
905 out.st.bfree = stat.f_bfree;
906 out.st.bavail = stat.f_bavail;
907 out.st.files = stat.f_files;
908 out.st.ffree = stat.f_ffree;
909 out.st.bsize = stat.f_bsize;
910 out.st.namelen = stat.f_namelen;
911 out.st.frsize = stat.f_frsize;
912 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700913 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700914}
915
Jeff Brown6249b902012-05-26 14:32:54 -0700916static int handle_release(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700917 const struct fuse_in_header* hdr, const struct fuse_release_in* req)
918{
919 struct handle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -0700920
921 TRACE("[%d] RELEASE %p(%d)\n", handler->token, h, h->fd);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700922 close(h->fd);
923 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -0700924 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700925}
926
Jeff Brown6249b902012-05-26 14:32:54 -0700927static int handle_fsync(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700928 const struct fuse_in_header* hdr, const struct fuse_fsync_in* req)
929{
930 int is_data_sync = req->fsync_flags & 1;
931 struct handle *h = id_to_ptr(req->fh);
932 int res;
Jeff Brown6249b902012-05-26 14:32:54 -0700933
934 TRACE("[%d] FSYNC %p(%d) is_data_sync=%d\n", handler->token,
935 h, h->fd, is_data_sync);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700936 res = is_data_sync ? fdatasync(h->fd) : fsync(h->fd);
937 if (res < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -0700938 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700939 }
Jeff Brown6249b902012-05-26 14:32:54 -0700940 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700941}
942
Jeff Brown6249b902012-05-26 14:32:54 -0700943static int handle_flush(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700944 const struct fuse_in_header* hdr)
945{
Jeff Brown6249b902012-05-26 14:32:54 -0700946 TRACE("[%d] FLUSH\n", handler->token);
947 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700948}
949
Jeff Brown6249b902012-05-26 14:32:54 -0700950static int handle_opendir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700951 const struct fuse_in_header* hdr, const struct fuse_open_in* req)
952{
Jeff Brown6249b902012-05-26 14:32:54 -0700953 struct node* node;
954 char path[PATH_MAX];
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700955 struct fuse_open_out out;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700956 struct dirhandle *h;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700957
Jeff Brown6249b902012-05-26 14:32:54 -0700958 pthread_mutex_lock(&fuse->lock);
959 node = lookup_node_and_path_by_id_locked(fuse, hdr->nodeid, path, sizeof(path));
960 TRACE("[%d] OPENDIR @ %llx (%s)\n", handler->token,
961 hdr->nodeid, node ? node->name : "?");
962 pthread_mutex_unlock(&fuse->lock);
963
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700964 if (!node) {
Jeff Brown6249b902012-05-26 14:32:54 -0700965 return -ENOENT;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700966 }
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700967 h = malloc(sizeof(*h));
968 if (!h) {
Jeff Brown6249b902012-05-26 14:32:54 -0700969 return -ENOMEM;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700970 }
Jeff Brown6249b902012-05-26 14:32:54 -0700971 TRACE("[%d] OPENDIR %s\n", handler->token, path);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700972 h->d = opendir(path);
Jeff Brown6249b902012-05-26 14:32:54 -0700973 if (!h->d) {
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700974 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -0700975 return -errno;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700976 }
977 out.fh = ptr_to_id(h);
978 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -0700979 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700980}
981
Jeff Brown6249b902012-05-26 14:32:54 -0700982static int handle_readdir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700983 const struct fuse_in_header* hdr, const struct fuse_read_in* req)
984{
985 char buffer[8192];
986 struct fuse_dirent *fde = (struct fuse_dirent*) buffer;
987 struct dirent *de;
988 struct dirhandle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -0700989
990 TRACE("[%d] READDIR %p\n", handler->token, h);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700991 if (req->offset == 0) {
992 /* rewinddir() might have been called above us, so rewind here too */
Jeff Brown6249b902012-05-26 14:32:54 -0700993 TRACE("[%d] calling rewinddir()\n", handler->token);
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700994 rewinddir(h->d);
995 }
996 de = readdir(h->d);
997 if (!de) {
Jeff Brown6249b902012-05-26 14:32:54 -0700998 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -0700999 }
1000 fde->ino = FUSE_UNKNOWN_INO;
1001 /* increment the offset so we can detect when rewinddir() seeks back to the beginning */
1002 fde->off = req->offset + 1;
1003 fde->type = de->d_type;
1004 fde->namelen = strlen(de->d_name);
1005 memcpy(fde->name, de->d_name, fde->namelen + 1);
1006 fuse_reply(fuse, hdr->unique, fde,
Jeff Brown6249b902012-05-26 14:32:54 -07001007 FUSE_DIRENT_ALIGN(sizeof(struct fuse_dirent) + fde->namelen));
1008 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001009}
1010
Jeff Brown6249b902012-05-26 14:32:54 -07001011static int handle_releasedir(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001012 const struct fuse_in_header* hdr, const struct fuse_release_in* req)
1013{
1014 struct dirhandle *h = id_to_ptr(req->fh);
Jeff Brown6249b902012-05-26 14:32:54 -07001015
1016 TRACE("[%d] RELEASEDIR %p\n", handler->token, h);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001017 closedir(h->d);
1018 free(h);
Jeff Brown6249b902012-05-26 14:32:54 -07001019 return 0;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001020}
1021
Jeff Brown6249b902012-05-26 14:32:54 -07001022static int handle_init(struct fuse* fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001023 const struct fuse_in_header* hdr, const struct fuse_init_in* req)
1024{
1025 struct fuse_init_out out;
1026
Jeff Brown6249b902012-05-26 14:32:54 -07001027 TRACE("[%d] INIT ver=%d.%d maxread=%d flags=%x\n",
1028 handler->token, req->major, req->minor, req->max_readahead, req->flags);
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001029 out.major = FUSE_KERNEL_VERSION;
1030 out.minor = FUSE_KERNEL_MINOR_VERSION;
1031 out.max_readahead = req->max_readahead;
1032 out.flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
1033 out.max_background = 32;
1034 out.congestion_threshold = 32;
1035 out.max_write = MAX_WRITE;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001036 fuse_reply(fuse, hdr->unique, &out, sizeof(out));
Jeff Brown6249b902012-05-26 14:32:54 -07001037 return NO_STATUS;
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001038}
1039
Jeff Brown6249b902012-05-26 14:32:54 -07001040static int handle_fuse_request(struct fuse *fuse, struct fuse_handler* handler,
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001041 const struct fuse_in_header *hdr, const void *data, size_t data_len)
1042{
Brian Swetland03ee9472010-08-12 18:01:08 -07001043 switch (hdr->opcode) {
1044 case FUSE_LOOKUP: { /* bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001045 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001046 return handle_lookup(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001047 }
Jeff Brown84715842012-05-25 14:07:47 -07001048
Brian Swetland03ee9472010-08-12 18:01:08 -07001049 case FUSE_FORGET: {
Jeff Brown84715842012-05-25 14:07:47 -07001050 const struct fuse_forget_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001051 return handle_forget(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001052 }
Jeff Brown84715842012-05-25 14:07:47 -07001053
Brian Swetland03ee9472010-08-12 18:01:08 -07001054 case FUSE_GETATTR: { /* getattr_in -> attr_out */
Jeff Brown84715842012-05-25 14:07:47 -07001055 const struct fuse_getattr_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001056 return handle_getattr(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001057 }
Jeff Brown84715842012-05-25 14:07:47 -07001058
Brian Swetland03ee9472010-08-12 18:01:08 -07001059 case FUSE_SETATTR: { /* setattr_in -> attr_out */
Jeff Brown84715842012-05-25 14:07:47 -07001060 const struct fuse_setattr_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001061 return handle_setattr(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001062 }
Jeff Brown84715842012-05-25 14:07:47 -07001063
Brian Swetland03ee9472010-08-12 18:01:08 -07001064// case FUSE_READLINK:
1065// case FUSE_SYMLINK:
1066 case FUSE_MKNOD: { /* mknod_in, bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001067 const struct fuse_mknod_in *req = data;
1068 const char *name = ((const char*) data) + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001069 return handle_mknod(fuse, handler, hdr, req, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001070 }
Jeff Brown84715842012-05-25 14:07:47 -07001071
Brian Swetland03ee9472010-08-12 18:01:08 -07001072 case FUSE_MKDIR: { /* mkdir_in, bytez[] -> entry_out */
Jeff Brown84715842012-05-25 14:07:47 -07001073 const struct fuse_mkdir_in *req = data;
1074 const char *name = ((const char*) data) + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001075 return handle_mkdir(fuse, handler, hdr, req, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001076 }
Jeff Brown84715842012-05-25 14:07:47 -07001077
Brian Swetland03ee9472010-08-12 18:01:08 -07001078 case FUSE_UNLINK: { /* bytez[] -> */
Jeff Brown84715842012-05-25 14:07:47 -07001079 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001080 return handle_unlink(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001081 }
Jeff Brown84715842012-05-25 14:07:47 -07001082
Brian Swetland03ee9472010-08-12 18:01:08 -07001083 case FUSE_RMDIR: { /* bytez[] -> */
Jeff Brown84715842012-05-25 14:07:47 -07001084 const char* name = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001085 return handle_rmdir(fuse, handler, hdr, name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001086 }
Jeff Brown84715842012-05-25 14:07:47 -07001087
Brian Swetland03ee9472010-08-12 18:01:08 -07001088 case FUSE_RENAME: { /* rename_in, oldname, newname -> */
Jeff Brown84715842012-05-25 14:07:47 -07001089 const struct fuse_rename_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001090 const char *old_name = ((const char*) data) + sizeof(*req);
1091 const char *new_name = old_name + strlen(old_name) + 1;
1092 return handle_rename(fuse, handler, hdr, req, old_name, new_name);
Brian Swetland03ee9472010-08-12 18:01:08 -07001093 }
Jeff Brown84715842012-05-25 14:07:47 -07001094
Jeff Brownfc1e1a02012-05-25 17:24:17 -07001095// case FUSE_LINK:
Brian Swetland03ee9472010-08-12 18:01:08 -07001096 case FUSE_OPEN: { /* open_in -> open_out */
Jeff Brown84715842012-05-25 14:07:47 -07001097 const struct fuse_open_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001098 return handle_open(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001099 }
Jeff Brown84715842012-05-25 14:07:47 -07001100
Brian Swetland03ee9472010-08-12 18:01:08 -07001101 case FUSE_READ: { /* read_in -> byte[] */
Jeff Brown84715842012-05-25 14:07:47 -07001102 const struct fuse_read_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001103 return handle_read(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001104 }
Jeff Brown84715842012-05-25 14:07:47 -07001105
Brian Swetland03ee9472010-08-12 18:01:08 -07001106 case FUSE_WRITE: { /* write_in, byte[write_in.size] -> write_out */
Jeff Brown84715842012-05-25 14:07:47 -07001107 const struct fuse_write_in *req = data;
1108 const void* buffer = (const __u8*)data + sizeof(*req);
Jeff Brown6249b902012-05-26 14:32:54 -07001109 return handle_write(fuse, handler, hdr, req, buffer);
Brian Swetland03ee9472010-08-12 18:01:08 -07001110 }
Jeff Brown84715842012-05-25 14:07:47 -07001111
Mike Lockwood4553b082010-08-16 14:14:44 -04001112 case FUSE_STATFS: { /* getattr_in -> attr_out */
Jeff Brown6249b902012-05-26 14:32:54 -07001113 return handle_statfs(fuse, handler, hdr);
Mike Lockwood4553b082010-08-16 14:14:44 -04001114 }
Jeff Brown84715842012-05-25 14:07:47 -07001115
Brian Swetland03ee9472010-08-12 18:01:08 -07001116 case FUSE_RELEASE: { /* release_in -> */
Jeff Brown84715842012-05-25 14:07:47 -07001117 const struct fuse_release_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001118 return handle_release(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001119 }
Jeff Brown84715842012-05-25 14:07:47 -07001120
Jeff Brown6fd921a2012-05-25 15:01:21 -07001121 case FUSE_FSYNC: {
1122 const struct fuse_fsync_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001123 return handle_fsync(fuse, handler, hdr, req);
Jeff Brown6fd921a2012-05-25 15:01:21 -07001124 }
1125
Brian Swetland03ee9472010-08-12 18:01:08 -07001126// case FUSE_SETXATTR:
1127// case FUSE_GETXATTR:
1128// case FUSE_LISTXATTR:
1129// case FUSE_REMOVEXATTR:
Jeff Brown84715842012-05-25 14:07:47 -07001130 case FUSE_FLUSH: {
Jeff Brown6249b902012-05-26 14:32:54 -07001131 return handle_flush(fuse, handler, hdr);
Jeff Brown84715842012-05-25 14:07:47 -07001132 }
1133
Brian Swetland03ee9472010-08-12 18:01:08 -07001134 case FUSE_OPENDIR: { /* open_in -> open_out */
Jeff Brown84715842012-05-25 14:07:47 -07001135 const struct fuse_open_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001136 return handle_opendir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001137 }
Jeff Brown84715842012-05-25 14:07:47 -07001138
Brian Swetland03ee9472010-08-12 18:01:08 -07001139 case FUSE_READDIR: {
Jeff Brown84715842012-05-25 14:07:47 -07001140 const struct fuse_read_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001141 return handle_readdir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001142 }
Jeff Brown84715842012-05-25 14:07:47 -07001143
Brian Swetland03ee9472010-08-12 18:01:08 -07001144 case FUSE_RELEASEDIR: { /* release_in -> */
Jeff Brown84715842012-05-25 14:07:47 -07001145 const struct fuse_release_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001146 return handle_releasedir(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001147 }
Jeff Brown84715842012-05-25 14:07:47 -07001148
Brian Swetland03ee9472010-08-12 18:01:08 -07001149// case FUSE_FSYNCDIR:
1150 case FUSE_INIT: { /* init_in -> init_out */
Jeff Brown84715842012-05-25 14:07:47 -07001151 const struct fuse_init_in *req = data;
Jeff Brown6249b902012-05-26 14:32:54 -07001152 return handle_init(fuse, handler, hdr, req);
Brian Swetland03ee9472010-08-12 18:01:08 -07001153 }
Jeff Brown84715842012-05-25 14:07:47 -07001154
Brian Swetland03ee9472010-08-12 18:01:08 -07001155 default: {
Jeff Brown6249b902012-05-26 14:32:54 -07001156 TRACE("[%d] NOTIMPL op=%d uniq=%llx nid=%llx\n",
1157 handler->token, hdr->opcode, hdr->unique, hdr->nodeid);
1158 return -ENOSYS;
Brian Swetland03ee9472010-08-12 18:01:08 -07001159 }
Jeff Brown84715842012-05-25 14:07:47 -07001160 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001161}
1162
Jeff Brown6249b902012-05-26 14:32:54 -07001163static void handle_fuse_requests(struct fuse_handler* handler)
Brian Swetland03ee9472010-08-12 18:01:08 -07001164{
Jeff Brown6249b902012-05-26 14:32:54 -07001165 struct fuse* fuse = handler->fuse;
Brian Swetland03ee9472010-08-12 18:01:08 -07001166 for (;;) {
Jeff Brown6249b902012-05-26 14:32:54 -07001167 ssize_t len = read(fuse->fd,
1168 handler->request_buffer, sizeof(handler->request_buffer));
Brian Swetland03ee9472010-08-12 18:01:08 -07001169 if (len < 0) {
Jeff Brown6249b902012-05-26 14:32:54 -07001170 if (errno != EINTR) {
1171 ERROR("[%d] handle_fuse_requests: errno=%d\n", handler->token, errno);
1172 }
1173 continue;
Brian Swetland03ee9472010-08-12 18:01:08 -07001174 }
Jeff Brown84715842012-05-25 14:07:47 -07001175
1176 if ((size_t)len < sizeof(struct fuse_in_header)) {
Jeff Brown6249b902012-05-26 14:32:54 -07001177 ERROR("[%d] request too short: len=%zu\n", handler->token, (size_t)len);
1178 continue;
Jeff Brown84715842012-05-25 14:07:47 -07001179 }
1180
Jeff Brown7729d242012-05-25 15:35:28 -07001181 const struct fuse_in_header *hdr = (void*)handler->request_buffer;
Jeff Brown84715842012-05-25 14:07:47 -07001182 if (hdr->len != (size_t)len) {
Jeff Brown6249b902012-05-26 14:32:54 -07001183 ERROR("[%d] malformed header: len=%zu, hdr->len=%u\n",
1184 handler->token, (size_t)len, hdr->len);
1185 continue;
Jeff Brown84715842012-05-25 14:07:47 -07001186 }
1187
Jeff Brown7729d242012-05-25 15:35:28 -07001188 const void *data = handler->request_buffer + sizeof(struct fuse_in_header);
Jeff Brown84715842012-05-25 14:07:47 -07001189 size_t data_len = len - sizeof(struct fuse_in_header);
Jeff Brown6249b902012-05-26 14:32:54 -07001190 __u64 unique = hdr->unique;
1191 int res = handle_fuse_request(fuse, handler, hdr, data, data_len);
Jeff Brown7729d242012-05-25 15:35:28 -07001192
1193 /* We do not access the request again after this point because the underlying
1194 * buffer storage may have been reused while processing the request. */
Jeff Brown6249b902012-05-26 14:32:54 -07001195
1196 if (res != NO_STATUS) {
1197 if (res) {
1198 TRACE("[%d] ERROR %d\n", handler->token, res);
1199 }
1200 fuse_status(fuse, unique, res);
1201 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001202 }
1203}
1204
Jeff Brown6249b902012-05-26 14:32:54 -07001205static void* start_handler(void* data)
Jeff Brown7729d242012-05-25 15:35:28 -07001206{
Jeff Brown6249b902012-05-26 14:32:54 -07001207 struct fuse_handler* handler = data;
1208 handle_fuse_requests(handler);
1209 return NULL;
1210}
1211
1212static int ignite_fuse(struct fuse* fuse, int num_threads)
1213{
1214 struct fuse_handler* handlers;
1215 int i;
1216
1217 handlers = malloc(num_threads * sizeof(struct fuse_handler));
1218 if (!handlers) {
1219 ERROR("cannot allocate storage for threads");
1220 return -ENOMEM;
1221 }
1222
1223 for (i = 0; i < num_threads; i++) {
1224 handlers[i].fuse = fuse;
1225 handlers[i].token = i;
1226 }
1227
1228 for (i = 1; i < num_threads; i++) {
1229 pthread_t thread;
1230 int res = pthread_create(&thread, NULL, start_handler, &handlers[i]);
1231 if (res) {
1232 ERROR("failed to start thread #%d, error=%d", i, res);
1233 goto quit;
1234 }
1235 }
1236 handle_fuse_requests(&handlers[0]);
1237 ERROR("terminated prematurely");
1238
1239 /* don't bother killing all of the other threads or freeing anything,
1240 * should never get here anyhow */
1241quit:
1242 exit(1);
Jeff Brown7729d242012-05-25 15:35:28 -07001243}
1244
Mike Lockwood4f35e622011-01-12 14:39:44 -05001245static int usage()
1246{
Jeff Brown6249b902012-05-26 14:32:54 -07001247 ERROR("usage: sdcard [-t<threads>] <path> <uid> <gid>\n"
1248 " -t<threads>: specify number of threads to use, default -t%d\n"
1249 "\n", DEFAULT_NUM_THREADS);
Jeff Brown26567352012-05-25 13:27:43 -07001250 return 1;
1251}
1252
Jeff Brown6249b902012-05-26 14:32:54 -07001253static int run(const char* path, uid_t uid, gid_t gid, int num_threads)
Jeff Brown26567352012-05-25 13:27:43 -07001254{
1255 int fd;
1256 char opts[256];
1257 int res;
1258 struct fuse fuse;
1259
1260 /* cleanup from previous instance, if necessary */
1261 umount2(MOUNT_POINT, 2);
1262
1263 fd = open("/dev/fuse", O_RDWR);
1264 if (fd < 0){
1265 ERROR("cannot open fuse device (error %d)\n", errno);
1266 return -1;
1267 }
1268
1269 snprintf(opts, sizeof(opts),
1270 "fd=%i,rootmode=40000,default_permissions,allow_other,user_id=%d,group_id=%d",
1271 fd, uid, gid);
1272
1273 res = mount("/dev/fuse", MOUNT_POINT, "fuse", MS_NOSUID | MS_NODEV, opts);
1274 if (res < 0) {
1275 ERROR("cannot mount fuse filesystem (error %d)\n", errno);
1276 goto error;
1277 }
1278
1279 res = setgid(gid);
1280 if (res < 0) {
1281 ERROR("cannot setgid (error %d)\n", errno);
1282 goto error;
1283 }
1284
1285 res = setuid(uid);
1286 if (res < 0) {
1287 ERROR("cannot setuid (error %d)\n", errno);
1288 goto error;
1289 }
1290
1291 fuse_init(&fuse, fd, path);
1292
1293 umask(0);
Jeff Brown6249b902012-05-26 14:32:54 -07001294 res = ignite_fuse(&fuse, num_threads);
Jeff Brown26567352012-05-25 13:27:43 -07001295
1296 /* we do not attempt to umount the file system here because we are no longer
1297 * running as the root user */
Jeff Brown26567352012-05-25 13:27:43 -07001298
1299error:
1300 close(fd);
1301 return res;
Mike Lockwood4f35e622011-01-12 14:39:44 -05001302}
1303
Brian Swetland03ee9472010-08-12 18:01:08 -07001304int main(int argc, char **argv)
1305{
Brian Swetland03ee9472010-08-12 18:01:08 -07001306 int res;
Mike Lockwood4f35e622011-01-12 14:39:44 -05001307 const char *path = NULL;
Jeff Brown26567352012-05-25 13:27:43 -07001308 uid_t uid = 0;
1309 gid_t gid = 0;
Jeff Brown6249b902012-05-26 14:32:54 -07001310 int num_threads = DEFAULT_NUM_THREADS;
Mike Lockwood4f35e622011-01-12 14:39:44 -05001311 int i;
Brian Swetland03ee9472010-08-12 18:01:08 -07001312
Mike Lockwood4f35e622011-01-12 14:39:44 -05001313 for (i = 1; i < argc; i++) {
1314 char* arg = argv[i];
Jeff Brown6249b902012-05-26 14:32:54 -07001315 if (!strncmp(arg, "-t", 2))
1316 num_threads = strtoul(arg + 2, 0, 10);
1317 else if (!path)
Mike Lockwood575a2bb2011-01-23 14:46:30 -08001318 path = arg;
Jeff Brown26567352012-05-25 13:27:43 -07001319 else if (!uid)
Mike Lockwood575a2bb2011-01-23 14:46:30 -08001320 uid = strtoul(arg, 0, 10);
Jeff Brown26567352012-05-25 13:27:43 -07001321 else if (!gid)
Mike Lockwood575a2bb2011-01-23 14:46:30 -08001322 gid = strtoul(arg, 0, 10);
1323 else {
1324 ERROR("too many arguments\n");
1325 return usage();
Mike Lockwood4f35e622011-01-12 14:39:44 -05001326 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001327 }
1328
Mike Lockwood4f35e622011-01-12 14:39:44 -05001329 if (!path) {
1330 ERROR("no path specified\n");
1331 return usage();
1332 }
Jeff Brown26567352012-05-25 13:27:43 -07001333 if (!uid || !gid) {
Brian Swetland03ee9472010-08-12 18:01:08 -07001334 ERROR("uid and gid must be nonzero\n");
Mike Lockwood4f35e622011-01-12 14:39:44 -05001335 return usage();
Brian Swetland03ee9472010-08-12 18:01:08 -07001336 }
Jeff Brown6249b902012-05-26 14:32:54 -07001337 if (num_threads < 1) {
1338 ERROR("number of threads must be at least 1\n");
1339 return usage();
1340 }
Brian Swetland03ee9472010-08-12 18:01:08 -07001341
Jeff Brown6249b902012-05-26 14:32:54 -07001342 res = run(path, uid, gid, num_threads);
Jeff Brown26567352012-05-25 13:27:43 -07001343 return res < 0 ? 1 : 0;
Brian Swetland03ee9472010-08-12 18:01:08 -07001344}