Merge "fuse: Improve error handling."
diff --git a/jni/FuseDaemon.cpp b/jni/FuseDaemon.cpp
index 8ea83a7..ba7f09a 100644
--- a/jni/FuseDaemon.cpp
+++ b/jni/FuseDaemon.cpp
@@ -101,7 +101,10 @@
};
struct dirhandle {
- DIR* d;
+ public:
+ explicit dirhandle(DIR* dir) : d(dir), next_off(0){};
+
+ DIR* const d;
off_t next_off;
// Fuse readdir() is called multiple times based on the size of the buffer and
// number of directory entries in the given directory. 'de' holds the list
@@ -559,17 +562,16 @@
return reinterpret_cast<struct fuse*>(fuse_req_userdata(req));
}
-static struct node* make_node_entry(fuse_req_t req,
- struct node* parent,
- const string& name,
- const string& path,
- struct fuse_entry_param* e) {
+static struct node* make_node_entry(fuse_req_t req, struct node* parent, const string& name,
+ const string& path, struct fuse_entry_param* e,
+ int* error_code) {
struct fuse* fuse = get_fuse(req);
const struct fuse_ctx* ctx = fuse_req_ctx(req);
struct node* node;
memset(e, 0, sizeof(*e));
if (lstat(path.c_str(), &e->attr) < 0) {
+ *error_code = errno;
return NULL;
}
@@ -652,17 +654,14 @@
}
static std::regex storage_emulated_regex("^\\/storage\\/emulated\\/([0-9]+)");
-static struct node* do_lookup(fuse_req_t req,
- fuse_ino_t parent,
- const char* name,
- struct fuse_entry_param* e) {
+static struct node* do_lookup(fuse_req_t req, fuse_ino_t parent, const char* name,
+ struct fuse_entry_param* e, int* error_code) {
struct fuse* fuse = get_fuse(req);
const struct fuse_ctx* ctx = fuse_req_ctx(req);
struct node* parent_node;
string parent_path;
string child_path;
- errno = 0;
{
std::lock_guard<std::mutex> lock(fuse->lock);
parent_node = lookup_node_by_id_locked(fuse, parent);
@@ -683,20 +682,23 @@
// So fail requests of two kinds:
// 1. /storage/emulated/0 coming to FuseDaemon running as user 10
// 2. /storage/emulated/0 requested from caller running as user 10
- errno = EPERM;
+ *error_code = EPERM;
return nullptr;
}
- return make_node_entry(req, parent_node, name, child_path, e);
+ return make_node_entry(req, parent_node, name, child_path, e, error_code);
}
static void pf_lookup(fuse_req_t req, fuse_ino_t parent, const char* name) {
ATRACE_CALL();
struct fuse_entry_param e;
- if (do_lookup(req, parent, name, &e))
+ int error_code = 0;
+ if (do_lookup(req, parent, name, &e, &error_code)) {
fuse_reply_entry(req, &e);
- else
- fuse_reply_err(req, errno);
+ } else {
+ CHECK(error_code != 0);
+ fuse_reply_err(req, error_code);
+ }
}
static void do_forget_locked(struct fuse* fuse, fuse_ino_t ino, uint64_t nlookup) {
@@ -872,10 +874,14 @@
fuse_reply_err(req, errno);
return;
}
- if (make_node_entry(req, parent_node, name, child_path, &e))
+
+ int error_code = 0;
+ if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
fuse_reply_entry(req, &e);
- else
- fuse_reply_err(req, errno);
+ } else {
+ CHECK(error_code != 0);
+ fuse_reply_err(req, error_code);
+ }
}
static void pf_mkdir(fuse_req_t req,
@@ -901,17 +907,25 @@
child_path = parent_path + "/" + name;
- errno = -fuse->mp->IsCreatingDirAllowed(child_path, ctx->uid);
+ int status = -fuse->mp->IsCreatingDirAllowed(child_path, ctx->uid);
+ if (status) {
+ fuse_reply_err(req, status);
+ return;
+ }
+
mode = (mode & (~0777)) | 0775;
- if (errno || mkdir(child_path.c_str(), mode) < 0) {
+ if (mkdir(child_path.c_str(), mode) < 0) {
fuse_reply_err(req, errno);
return;
}
- if (make_node_entry(req, parent_node, name, child_path, &e))
+ int error_code = 0;
+ if (make_node_entry(req, parent_node, name, child_path, &e, &error_code)) {
fuse_reply_entry(req, &e);
- else
- fuse_reply_err(req, errno);
+ } else {
+ CHECK(error_code != 0);
+ fuse_reply_err(req, error_code);
+ }
}
static void pf_unlink(fuse_req_t req, fuse_ino_t parent, const char* name) {
@@ -932,9 +946,9 @@
child_path = parent_path + "/" + name;
- errno = -fuse->mp->DeleteFile(child_path, ctx->uid);
- if (errno) {
- fuse_reply_err(req, errno);
+ int status = -fuse->mp->DeleteFile(child_path, ctx->uid);
+ if (status) {
+ fuse_reply_err(req, status);
return;
}
@@ -967,8 +981,13 @@
child_path = parent_path + "/" + name;
- errno = -fuse->mp->IsDeletingDirAllowed(child_path, ctx->uid);
- if (errno || rmdir(child_path.c_str()) < 0) {
+ int status = -fuse->mp->IsDeletingDirAllowed(child_path, ctx->uid);
+ if (status) {
+ fuse_reply_err(req, status);
+ return;
+ }
+
+ if (rmdir(child_path.c_str()) < 0) {
fuse_reply_err(req, errno);
return;
}
@@ -1067,7 +1086,6 @@
struct fuse* fuse = get_fuse(req);
const struct fuse_ctx* ctx = fuse_req_ctx(req);
struct fuse_open_out out;
- handle* h;
{
std::lock_guard<std::mutex> lock(fuse->lock);
@@ -1088,28 +1106,36 @@
}
TRACE_FUSE(fuse) << "OPEN " << path;
- h = new handle(path);
- errno = -fuse->mp->IsOpenAllowed(h->path, ctx->uid, is_requesting_write(fi->flags));
- if (errno || (h->fd = open(path.c_str(), fi->flags)) < 0) {
- delete h;
+ int status = -fuse->mp->IsOpenAllowed(path, ctx->uid, is_requesting_write(fi->flags));
+ if (status) {
+ fuse_reply_err(req, status);
+ return;
+ }
+
+ const int fd = open(path.c_str(), fi->flags);
+ if (fd < 0) {
fuse_reply_err(req, errno);
return;
}
// We don't redact if the caller was granted write permission for this file
+ std::unique_ptr<RedactionInfo> ri;
if (is_requesting_write(fi->flags)) {
- h->ri = std::make_unique<RedactionInfo>();
+ ri = std::make_unique<RedactionInfo>();
} else {
- h->ri = fuse->mp->GetRedactionInfo(h->path, req->ctx.uid);
+ ri = fuse->mp->GetRedactionInfo(path, req->ctx.uid);
}
- if (!h->ri) {
- errno = EFAULT;
- close(h->fd);
- fuse_reply_err(req, errno);
+ if (!ri) {
+ close(fd);
+ fuse_reply_err(req, EFAULT);
return;
}
+ handle* h = new handle(path);
+ h->fd = fd;
+ h->ri = std::move(ri);
+
if (h->ri->isRedactionNeeded() || is_file_locked(h->fd, path)) {
// We don't want to use the FUSE VFS cache in two cases:
// 1. When redaction is needed because app A with EXIF access might access
@@ -1412,15 +1438,21 @@
return;
}
- h = new dirhandle();
- errno = -fuse->mp->IsOpendirAllowed(path, ctx->uid);
- if (errno || !(h->d = opendir(path.c_str()))) {
- delete h;
+ int status = -fuse->mp->IsOpendirAllowed(path, ctx->uid);
+ if (status) {
+ fuse_reply_err(req, status);
+ return;
+ }
+
+ DIR* dir = opendir(path.c_str());
+ if (!dir) {
fuse_reply_err(req, errno);
return;
}
+
+ h = new dirhandle(dir);
node->dirhandles.push_back(h);
- h->next_off = 0;
+
fi->fh = ptr_to_id(h);
fuse_reply_open(req, fi);
}
@@ -1439,7 +1471,6 @@
char buf[READDIR_BUF];
size_t used = 0;
std::shared_ptr<DirectoryEntry> de;
- errno = 0;
struct fuse_entry_param e;
size_t entry_size = 0;
@@ -1482,16 +1513,16 @@
entry_size = 0;
h->next_off++;
if (plus) {
- errno = 0;
- if (do_lookup(req, ino, de->d_name.c_str(), &e)) {
+ int error_code = 0;
+ if (do_lookup(req, ino, de->d_name.c_str(), &e, &error_code)) {
entry_size = fuse_add_direntry_plus(req, buf + used, len - used, de->d_name.c_str(),
&e, h->next_off);
} else {
// Ignore lookup errors on
// 1. non-existing files returned from MediaProvider database.
// 2. path that doesn't match FuseDaemon UID and calling uid.
- if (errno == ENOENT || errno == EPERM) continue;
- fuse_reply_err(req, errno);
+ if (error_code == ENOENT || error_code == EPERM) continue;
+ fuse_reply_err(req, error_code);
return;
}
} else {
@@ -1633,31 +1664,37 @@
child_path = parent_path + "/" + name;
- h = new handle(child_path);
- mode = (mode & (~0777)) | 0664;
- int mp_return_code = fuse->mp->InsertFile(child_path.c_str(), ctx->uid);
- if (mp_return_code || ((h->fd = open(child_path.c_str(), fi->flags, mode)) < 0)) {
- if (mp_return_code) {
- errno = -mp_return_code;
- // In this case, we know open was not called.
- } else {
- // In this case, we know that open has failed, so we want to undo the file insertion.
- fuse->mp->DeleteFile(child_path.c_str(), ctx->uid);
- }
- delete h;
+ int mp_return_code = -fuse->mp->InsertFile(child_path.c_str(), ctx->uid);
+ if (mp_return_code) {
PLOG(DEBUG) << "Could not create file: " << child_path;
- fuse_reply_err(req, errno);
+ fuse_reply_err(req, mp_return_code);
return;
}
+ mode = (mode & (~0777)) | 0664;
+ int fd = open(child_path.c_str(), fi->flags, mode);
+ if (fd < 0) {
+ int error_code = errno;
+ // We've already inserted the file into the MP database before the
+ // failed open(), so that needs to be rolled back here.
+ fuse->mp->DeleteFile(child_path.c_str(), ctx->uid);
+ fuse_reply_err(req, error_code);
+ return;
+ }
+
+ h = new handle(child_path);
+ h->fd = fd;
fi->fh = ptr_to_id(h);
fi->keep_cache = 1;
- node* node = make_node_entry(req, parent_node, name, child_path, &e);
+
+ int error_code = 0;
+ node* node = make_node_entry(req, parent_node, name, child_path, &e, &error_code);
if (node) {
node->handles.push_back(h);
fuse_reply_create(req, &e, fi);
} else {
- fuse_reply_err(req, errno);
+ CHECK(error_code != 0);
+ fuse_reply_err(req, error_code);
}
}
/*