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
This commit is contained in:
Krzysztof Adamski 2014-07-16 08:34:30 +02:00 committed by Johan Redestig
parent 1158c7d5fe
commit c5353126be
1 changed files with 19 additions and 1 deletions

View File

@ -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;
}