Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c5353126 authored by Krzysztof Adamski's avatar Krzysztof Adamski Committed by Johan Redestig
Browse files

sdcard: Properly handle deleted nodes

The sdcard fuse deamon is not properly handling deleted nodes that are
still in use (opened by some process). Typically Linux filesystems makes
it possible to open a file, unlink it and then still use it. In case of a
storage emulated by sdcard deamon this does not work as expected - other
process are not able to recreate file/dir with the same name until all
references to deleted file are closed.

The easiest way to trigger this problem is:

process1: mkdir /sdcard/test1; cd /sdcard/test1
process2: rm -r /sdcard/test1
process2: mkdir /sdcard/test1

After that, process2 will get an error:
mkdir failed for /sdcard/test1, Device or resource busy

There is exactly the same problem with files as directories.
This may case issues for example with directories that are
automatically recreated when they are missing (like DCIM directory). If
some process holds file opened inside of such directory but that
directory is removed, process trying to recreate the directory will get
EBUSY error and possibly crash.

Verified on the Z Ultra GPE.

Change-Id: I1cbf0bec135e6aaafba0ce8e5bb594e3639e0007
parent 1158c7d5
Loading
Loading
Loading
Loading
+19 −1
Original line number Diff line number Diff line
@@ -199,6 +199,8 @@ struct node {
     * position. Used to support things like OBB. */
    char* graft_path;
    size_t graft_pathlen;

    bool deleted;
};

static int str_hash(void *key) {
@@ -631,6 +633,8 @@ struct node *create_node_locked(struct fuse* fuse,
    node->ino = fuse->inode_ctr++;
    node->gen = fuse->next_generation++;

    node->deleted = false;

    derive_permissions_locked(fuse, parent, node);
    acquire_node_locked(node);
    add_node_to_parent_locked(node, parent);
@@ -704,7 +708,7 @@ static struct node *lookup_child_by_name_locked(struct node *node, const char *n
         * must be considered distinct even if they refer to the same
         * underlying file as otherwise operations such as "mv x x"
         * will not work because the source and target nodes are the same. */
        if (!strcmp(name, node->name)) {
        if (!strcmp(name, node->name) && !node->deleted) {
            return node;
        }
    }
@@ -1070,6 +1074,7 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
{
    bool has_rw;
    struct node* parent_node;
    struct node* child_node;
    char parent_path[PATH_MAX];
    char child_path[PATH_MAX];

@@ -1091,6 +1096,12 @@ static int handle_unlink(struct fuse* fuse, struct fuse_handler* handler,
    if (unlink(child_path) < 0) {
        return -errno;
    }
    pthread_mutex_lock(&fuse->lock);
    child_node = lookup_child_by_name_locked(parent_node, name);
    if (child_node) {
        child_node->deleted = true;
    }
    pthread_mutex_unlock(&fuse->lock);
    return 0;
}

@@ -1098,6 +1109,7 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
        const struct fuse_in_header* hdr, const char* name)
{
    bool has_rw;
    struct node* child_node;
    struct node* parent_node;
    char parent_path[PATH_MAX];
    char child_path[PATH_MAX];
@@ -1120,6 +1132,12 @@ static int handle_rmdir(struct fuse* fuse, struct fuse_handler* handler,
    if (rmdir(child_path) < 0) {
        return -errno;
    }
    pthread_mutex_lock(&fuse->lock);
    child_node = lookup_child_by_name_locked(parent_node, name);
    if (child_node) {
        child_node->deleted = true;
    }
    pthread_mutex_unlock(&fuse->lock);
    return 0;
}