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:
parent
1158c7d5fe
commit
c5353126be
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue