fix
diff --git a/ChangeLog b/ChangeLog
index 963df14..f246add 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2004-09-16 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Check memory allocation failures in libfuse
+
2004-09-14 Miklos Szeredi <miklos@szeredi.hu>
* Check temporary file creation failure in do_getdir(). Bug
diff --git a/include/fuse.h b/include/fuse.h
index 845e645..81ef0d4 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -231,8 +231,9 @@
* operations are called.
*
* @param f the FUSE handle
+ * @return 0 if no error occured, -1 otherwise
*/
-void fuse_loop(struct fuse *f);
+int fuse_loop(struct fuse *f);
/**
* Exit from event loop
@@ -252,8 +253,9 @@
* the application.
*
* @param f the FUSE handle
+ * @return 0 if no error occured, -1 otherwise
*/
-void fuse_loop_mt(struct fuse *f);
+int fuse_loop_mt(struct fuse *f);
/**
* Get the current context
@@ -284,7 +286,7 @@
typedef void (*fuse_processor_t)(struct fuse *, struct fuse_cmd *, void *);
struct fuse_cmd *__fuse_read_cmd(struct fuse *f);
void __fuse_process_cmd(struct fuse *f, struct fuse_cmd *cmd);
-void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data);
+int __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data);
int __fuse_exited(struct fuse* f);
#ifdef __cplusplus
diff --git a/lib/fuse.c b/lib/fuse.c
index 659643b..a207fc0 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -159,14 +159,18 @@
abort();
}
-static void hash_name(struct fuse *f, struct node *node, fino_t parent,
- const char *name)
+static int hash_name(struct fuse *f, struct node *node, fino_t parent,
+ const char *name)
{
size_t hash = name_hash(f, parent, name);
node->parent = parent;
node->name = strdup(name);
+ if (node->name == NULL)
+ return -1;
+
node->name_next = f->name_table[hash];
- f->name_table[hash] = node;
+ f->name_table[hash] = node;
+ return 0;
}
static void unhash_name(struct fuse *f, struct node *node)
@@ -210,14 +214,20 @@
}
node = (struct node *) calloc(1, sizeof(struct node));
+ if (node == NULL)
+ return NULL;
+
node->mode = mode;
node->rdev = rdev;
node->open_count = 0;
node->is_hidden = 0;
node->ino = next_ino(f);
node->generation = f->generation;
+ if (hash_name(f, node, parent, name) == -1) {
+ free(node);
+ return NULL;
+ }
hash_ino(f, node);
- hash_name(f, node, parent, name);
out:
node->version = version;
@@ -361,14 +371,18 @@
if (newnode != NULL) {
if (hide) {
fprintf(stderr, "fuse: hidden file got created during hiding\n");
- err = -1;
+ err = -EBUSY;
goto out;
}
unhash_name(f, newnode);
}
unhash_name(f, node);
- hash_name(f, node, newdir, newname);
+ if (hash_name(f, node, newdir, newname) == -1) {
+ err = -ENOMEM;
+ goto out;
+ }
+
if (hide)
node->is_hidden = 1;
@@ -464,15 +478,20 @@
outsize = sizeof(struct fuse_out_header) + argsize;
outbuf = (char *) malloc(outsize);
- out = (struct fuse_out_header *) outbuf;
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = error;
- if (argsize != 0)
- memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
-
- res = send_reply_raw(f, outbuf, outsize, locked);
- free(outbuf);
+ if (outbuf == NULL) {
+ fprintf(stderr, "fuse: failed to allocate reply buffer\n");
+ res = -ENOMEM;
+ } else {
+ out = (struct fuse_out_header *) outbuf;
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = error;
+ if (argsize != 0)
+ memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
+
+ res = send_reply_raw(f, outbuf, outsize, locked);
+ free(outbuf);
+ }
return res;
}
@@ -538,22 +557,18 @@
{
char newname[64];
char *newpath;
- int err = -1;
+ int err = -EBUSY;
- if (!f->op.rename || !f->op.unlink)
- return -EBUSY;
-
- newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
- if (newpath) {
- err = f->op.rename(oldpath, newpath);
- if (!err)
- err = rename_node(f, dir, oldname, dir, newname, 1);
- free(newpath);
+ if (f->op.rename && f->op.unlink) {
+ newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
+ if (newpath) {
+ int res = f->op.rename(oldpath, newpath);
+ if (res == 0)
+ err = rename_node(f, dir, oldname, dir, newname, 1);
+ free(newpath);
+ }
}
- if (err)
- return -EBUSY;
-
- return 0;
+ return err;
}
static int lookup_path(struct fuse *f, fino_t ino, int version, char *name,
@@ -569,15 +584,19 @@
memset(arg, 0, sizeof(struct fuse_entry_out));
convert_stat(&buf, &arg->attr);
node = find_node(f, ino, name, &arg->attr, version);
- arg->ino = node->ino;
- arg->generation = node->generation;
- arg->entry_valid = ENTRY_REVALIDATE_TIME;
- arg->entry_valid_nsec = 0;
- arg->attr_valid = ATTR_REVALIDATE_TIME;
- arg->attr_valid_nsec = 0;
- if (f->flags & FUSE_DEBUG) {
- printf(" INO: %li\n", arg->ino);
- fflush(stdout);
+ if (node == NULL)
+ res = -ENOMEM;
+ else {
+ arg->ino = node->ino;
+ arg->generation = node->generation;
+ arg->entry_valid = ENTRY_REVALIDATE_TIME;
+ arg->entry_valid_nsec = 0;
+ arg->attr_valid = ATTR_REVALIDATE_TIME;
+ arg->attr_valid_nsec = 0;
+ if (f->flags & FUSE_DEBUG) {
+ printf(" INO: %li\n", arg->ino);
+ fflush(stdout);
+ }
}
}
return res;
@@ -950,7 +969,7 @@
if (res == 0) {
res = f->op.rename(oldpath, newpath);
if (res == 0)
- rename_node(f, olddir, oldname, newdir, newname, 0);
+ res = rename_node(f, olddir, oldname, newdir, newname, 0);
}
}
free(newpath);
@@ -1096,42 +1115,46 @@
int res;
char *path;
char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *buf = outbuf + sizeof(struct fuse_out_header);
- size_t size;
- size_t outsize;
-
- res = -ENOENT;
- path = get_path(f, in->ino);
- if (path != NULL) {
- if (f->flags & FUSE_DEBUG) {
- printf("READ[%u] %u bytes from %llu\n", arg->fh, arg->size,
- arg->offset);
- fflush(stdout);
+ if (outbuf == NULL)
+ send_reply(f, in, -ENOMEM, NULL, 0);
+ else {
+ struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ char *buf = outbuf + sizeof(struct fuse_out_header);
+ size_t size;
+ size_t outsize;
+
+ res = -ENOENT;
+ path = get_path(f, in->ino);
+ if (path != NULL) {
+ if (f->flags & FUSE_DEBUG) {
+ printf("READ[%u] %u bytes from %llu\n", arg->fh, arg->size,
+ arg->offset);
+ fflush(stdout);
+ }
+
+ res = -ENOSYS;
+ if (f->op.read)
+ res = f->op.read(path, buf, arg->size, arg->offset);
+ free(path);
}
-
- res = -ENOSYS;
- if (f->op.read)
- res = f->op.read(path, buf, arg->size, arg->offset);
- free(path);
- }
-
- size = 0;
- if (res >= 0) {
- size = res;
- res = 0;
- if (f->flags & FUSE_DEBUG) {
- printf(" READ[%u] %u bytes\n", arg->fh, size);
- fflush(stdout);
+
+ size = 0;
+ if (res >= 0) {
+ size = res;
+ res = 0;
+ if (f->flags & FUSE_DEBUG) {
+ printf(" READ[%u] %u bytes\n", arg->fh, size);
+ fflush(stdout);
+ }
}
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = res;
+ outsize = sizeof(struct fuse_out_header) + size;
+
+ send_reply_raw(f, outbuf, outsize, 0);
+ free(outbuf);
}
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
- outsize = sizeof(struct fuse_out_header) + size;
-
- send_reply_raw(f, outbuf, outsize, 0);
- free(outbuf);
}
static void do_write(struct fuse *f, struct fuse_in_header *in,
@@ -1263,21 +1286,25 @@
{
int res;
char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + size);
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *value = outbuf + sizeof(struct fuse_out_header);
-
- res = common_getxattr(f, in, name, value, size);
- size = 0;
- if (res > 0) {
- size = res;
- res = 0;
+ if (outbuf == NULL)
+ send_reply(f, in, -ENOMEM, NULL, 0);
+ else {
+ struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ char *value = outbuf + sizeof(struct fuse_out_header);
+
+ res = common_getxattr(f, in, name, value, size);
+ size = 0;
+ if (res > 0) {
+ size = res;
+ res = 0;
+ }
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = res;
+
+ send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+ free(outbuf);
}
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
-
- send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
- free(outbuf);
}
static void do_getxattr_size(struct fuse *f, struct fuse_in_header *in,
@@ -1327,21 +1354,25 @@
{
int res;
char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + size);
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *list = outbuf + sizeof(struct fuse_out_header);
-
- res = common_listxattr(f, in, list, size);
- size = 0;
- if (res > 0) {
- size = res;
- res = 0;
+ if (outbuf == NULL)
+ send_reply(f, in, -ENOMEM, NULL, 0);
+ else {
+ struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ char *list = outbuf + sizeof(struct fuse_out_header);
+
+ res = common_listxattr(f, in, list, size);
+ size = 0;
+ if (res > 0) {
+ size = res;
+ res = 0;
+ }
+ memset(out, 0, sizeof(struct fuse_out_header));
+ out->unique = in->unique;
+ out->error = res;
+
+ send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
+ free(outbuf);
}
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
-
- send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size, 0);
- free(outbuf);
}
static void do_listxattr_size(struct fuse *f, struct fuse_in_header *in)
@@ -1525,7 +1556,16 @@
void *inarg;
cmd = (struct fuse_cmd *) malloc(sizeof(struct fuse_cmd));
+ if (cmd == NULL) {
+ fprintf(stderr, "fuse: failed to allocate cmd in read\n");
+ return NULL;
+ }
cmd->buf = (char *) malloc(FUSE_MAX_IN);
+ if (cmd->buf == NULL) {
+ fprintf(stderr, "fuse: failed to allocate read buffer\n");
+ free(cmd);
+ return NULL;
+ }
in = (struct fuse_in_header *) cmd->buf;
inarg = cmd->buf + sizeof(struct fuse_in_header);
@@ -1563,16 +1603,16 @@
return cmd;
}
-void fuse_loop(struct fuse *f)
+int fuse_loop(struct fuse *f)
{
if (f == NULL)
- return;
+ return -1;
while (1) {
struct fuse_cmd *cmd;
if (__fuse_exited(f))
- return;
+ return 0;
cmd = __fuse_read_cmd(f);
if (cmd == NULL)
@@ -1580,6 +1620,7 @@
__fuse_process_cmd(f, cmd);
}
+ return 0;
}
int fuse_invalidate(struct fuse *f, const char *path)
@@ -1668,12 +1709,15 @@
return 0;
}
-static void parse_lib_opts(struct fuse *f, const char *opts)
+static int parse_lib_opts(struct fuse *f, const char *opts)
{
if (opts) {
char *xopts = strdup(opts);
char *s = xopts;
char *opt;
+
+ if (xopts == NULL)
+ return -1;
while((opt = strsep(&s, ","))) {
if (strcmp(opt, "debug") == 0)
@@ -1685,6 +1729,7 @@
}
free(xopts);
}
+ return 0;
}
struct fuse *fuse_new(int fd, const char *opts, const struct fuse_operations *op)
@@ -1693,13 +1738,15 @@
struct node *root;
f = (struct fuse *) calloc(1, sizeof(struct fuse));
+ if (f == NULL)
+ goto out;
- if (check_version(f) == -1) {
- free(f);
- return NULL;
- }
+ if (check_version(f) == -1)
+ goto out_free;
- parse_lib_opts(f, opts);
+ if (parse_lib_opts(f, opts) == -1)
+ goto out_free;
+
f->fd = fd;
f->ctr = 0;
f->generation = 0;
@@ -1707,9 +1754,15 @@
f->name_table_size = 14057;
f->name_table = (struct node **)
calloc(1, sizeof(struct node *) * f->name_table_size);
+ if (f->name_table == NULL)
+ goto out_free;
+
f->ino_table_size = 14057;
f->ino_table = (struct node **)
calloc(1, sizeof(struct node *) * f->ino_table_size);
+ if (f->ino_table == NULL)
+ goto out_free_name_table;
+
pthread_mutex_init(&f->lock, NULL);
f->numworker = 0;
f->numavail = 0;
@@ -1720,15 +1773,33 @@
f->exited = 0;
root = (struct node *) calloc(1, sizeof(struct node));
+ if (root == NULL)
+ goto out_free_ino_table;
+
root->mode = 0;
root->rdev = 0;
root->name = strdup("/");
+ if (root->name == NULL)
+ goto out_free_root;
+
root->parent = 0;
root->ino = FUSE_ROOT_INO;
root->generation = 0;
hash_ino(f, root);
return f;
+
+ out_free_root:
+ free(root);
+ out_free_ino_table:
+ free(f->ino_table);
+ out_free_name_table:
+ free(f->name_table);
+ out_free:
+ free(f);
+ out:
+ fprintf(stderr, "fuse: failed to allocate fuse object\n");
+ return NULL;
}
void fuse_destroy(struct fuse *f)
diff --git a/lib/fuse_mt.c b/lib/fuse_mt.c
index 71196a9..8bac5ff 100644
--- a/lib/fuse_mt.c
+++ b/lib/fuse_mt.c
@@ -25,12 +25,23 @@
fuse_processor_t proc;
};
-static void start_thread(struct fuse_worker *w, pthread_t *thread_id);
+static int start_thread(struct fuse_worker *w, pthread_t *thread_id);
static void *do_work(void *data)
{
struct fuse_worker *w = (struct fuse_worker *) data;
struct fuse *f = w->f;
+ struct fuse_context *ctx;
+
+ ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context));
+ if (ctx == NULL) {
+ fprintf(stderr, "fuse: failed to allocate fuse context\n");
+ pthread_mutex_lock(&f->lock);
+ f->numavail --;
+ pthread_mutex_unlock(&f->lock);
+ return NULL;
+ }
+ pthread_setspecific(f->context_key, ctx);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
@@ -48,11 +59,19 @@
if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
pthread_mutex_lock(&f->lock);
if (f->numworker < FUSE_MAX_WORKERS) {
+ /* FIXME: threads should be stored in a list instead
+ of an array */
+ int res;
pthread_t *thread_id = &w->threads[f->numworker];
f->numavail ++;
f->numworker ++;
pthread_mutex_unlock(&f->lock);
- start_thread(w, thread_id);
+ res = start_thread(w, thread_id);
+ if (res == -1) {
+ pthread_mutex_lock(&f->lock);
+ f->numavail --;
+ pthread_mutex_unlock(&f->lock);
+ }
} else
pthread_mutex_unlock(&f->lock);
}
@@ -63,7 +82,7 @@
return NULL;
}
-static void start_thread(struct fuse_worker *w, pthread_t *thread_id)
+static int start_thread(struct fuse_worker *w, pthread_t *thread_id)
{
sigset_t oldset;
sigset_t newset;
@@ -76,23 +95,16 @@
pthread_sigmask(SIG_SETMASK, &oldset, NULL);
if (res != 0) {
fprintf(stderr, "Error creating thread: %s\n", strerror(res));
- exit(1);
+ return -1;
}
pthread_detach(*thread_id);
+ return 0;
}
static struct fuse_context *mt_getcontext(struct fuse *f)
{
- struct fuse_context *ctx;
-
- ctx = (struct fuse_context *) pthread_getspecific(f->context_key);
- if (ctx == NULL) {
- ctx = (struct fuse_context *) malloc(sizeof(struct fuse_context));
- pthread_setspecific(f->context_key, ctx);
- }
-
- return ctx;
+ return (struct fuse_context *) pthread_getspecific(f->context_key);
}
static void mt_freecontext(void *data)
@@ -100,13 +112,17 @@
free(data);
}
-void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
+int __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
{
struct fuse_worker *w;
int res;
int i;
w = malloc(sizeof(struct fuse_worker));
+ if (w == NULL) {
+ fprintf(stderr, "fuse: failed to allocate worker structure\n");
+ return -1;
+ }
memset(w, 0, sizeof(struct fuse_worker));
w->f = f;
w->data = data;
@@ -116,7 +132,7 @@
res = pthread_key_create(&f->context_key, mt_freecontext);
if (res != 0) {
fprintf(stderr, "Failed to create thread specific key\n");
- exit(1);
+ return -1;
}
f->getcontext = mt_getcontext;
do_work(w);
@@ -127,12 +143,13 @@
pthread_mutex_unlock(&f->lock);
pthread_key_delete(f->context_key);
free(w);
+ return 0;
}
-void fuse_loop_mt(struct fuse *f)
+int fuse_loop_mt(struct fuse *f)
{
if (f == NULL)
- return;
+ return -1;
- __fuse_loop_mt(f, (fuse_processor_t) __fuse_process_cmd, NULL);
+ return __fuse_loop_mt(f, (fuse_processor_t) __fuse_process_cmd, NULL);
}
diff --git a/lib/helper.c b/lib/helper.c
index b75855a..70c4bd8 100644
--- a/lib/helper.c
+++ b/lib/helper.c
@@ -119,18 +119,23 @@
return 0;
}
-static void add_option_to(const char *opt, char **optp)
+static int add_option_to(const char *opt, char **optp)
{
unsigned len = strlen(opt);
if (*optp) {
unsigned oldlen = strlen(*optp);
*optp = realloc(*optp, oldlen + 1 + len + 1);
+ if (*optp == NULL)
+ return -1;
(*optp)[oldlen] = ',';
strcpy(*optp + oldlen + 1, opt);
} else {
*optp = malloc(len + 1);
+ if (*optp == NULL)
+ return -1;
strcpy(*optp, opt);
}
+ return 0;
}
static void add_options(char **lib_optp, char **kernel_optp, const char *opts)
@@ -138,12 +143,22 @@
char *xopts = strdup(opts);
char *s = xopts;
char *opt;
-
+
+ if (xopts == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ exit(1);
+ }
+
while((opt = strsep(&s, ",")) != NULL) {
+ int res;
if (fuse_is_lib_option(opt))
- add_option_to(opt, lib_optp);
+ res = add_option_to(opt, lib_optp);
else
- add_option_to(opt, kernel_optp);
+ res = add_option_to(opt, kernel_optp);
+ if (res == -1) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ exit(1);
+ }
}
free(xopts);
}
@@ -168,6 +183,10 @@
basename++;
fsname_opt = malloc(strlen(basename) + 64);
+ if (fsname_opt == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ exit(1);
+ }
sprintf(fsname_opt, "fsname=%s", basename);
add_options(&lib_opts, &kernel_opts, fsname_opt);
free(fsname_opt);
@@ -213,8 +232,13 @@
else
invalid_option(argv, argctr);
}
- } else if (fuse_mountpoint == NULL)
+ } else if (fuse_mountpoint == NULL) {
fuse_mountpoint = strdup(argv[argctr]);
+ if (fuse_mountpoint == NULL) {
+ fprintf(stderr, "fuse: memory allocation failed\n");
+ exit(1);
+ }
+ }
else
invalid_option(argv, argctr);
}