ANDROID: sdcardfs: Replace get/put with d_lock
authorDaniel Rosenberg <drosen@google.com>
Thu, 2 Mar 2017 23:11:27 +0000 (15:11 -0800)
committerStricted <info@stricted.net>
Thu, 11 Oct 2018 16:03:27 +0000 (18:03 +0200)
dput cannot be called with a spin_lock. Instead,
we protect our accesses by holding the d_lock.

Signed-off-by: Daniel Rosenberg <drosen@google.com>
Bug: 35643557
Change-Id: I22cf30856d75b5616cbb0c223724f5ab866b5114

fs/sdcardfs/derived_perm.c

index 5ad69e6f6c3395d3a10aa22200d2a90c0d908f2c..cdf31c95199ebe5a4147434b35a82d606e9ba790 100644 (file)
@@ -254,40 +254,46 @@ static int needs_fixup(perm_t perm) {
        return 0;
 }
 
-void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
+static void __fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit, int depth) {
        struct dentry *child;
        struct sdcardfs_inode_info *info;
-       if (!dget(dentry))
-               return;
+
+       /*
+        * All paths will terminate their recursion on hitting PERM_ANDROID_OBB,
+        * PERM_ANDROID_MEDIA, or PERM_ANDROID_DATA. This happens at a depth of
+        * at most 3.
+        */
+       WARN(depth > 3, "%s: Max expected depth exceeded!\n", __func__);
+       spin_lock_nested(&dentry->d_lock, depth);
        if (!dentry->d_inode) {
-               dput(dentry);
+               spin_unlock(&dentry->d_lock);
                return;
        }
        info = SDCARDFS_I(dentry->d_inode);
 
        if (needs_fixup(info->perm)) {
-               spin_lock(&dentry->d_lock);
                list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
-                       dget(child);
+                       spin_lock_nested(&child->d_lock, depth + 1);
                        if (!(limit->flags & BY_NAME) || !strncasecmp(child->d_name.name, limit->name, limit->length)) {
                                if (child->d_inode) {
                                        get_derived_permission(dentry, child);
                                        fixup_tmp_permissions(child->d_inode);
-                                       dput(child);
+                                       spin_unlock(&child->d_lock);
                                        break;
                                }
                        }
-                       dput(child);
+                       spin_unlock(&child->d_lock);
                }
-               spin_unlock(&dentry->d_lock);
        } else  if (descendant_may_need_fixup(info, limit)) {
-               spin_lock(&dentry->d_lock);
                list_for_each_entry(child, &dentry->d_subdirs, d_u.d_child) {
-                               fixup_perms_recursive(child, limit);
+                               __fixup_perms_recursive(child, limit, depth + 1);
                }
-               spin_unlock(&dentry->d_lock);
        }
-       dput(dentry);
+       spin_unlock(&dentry->d_lock);
+}
+
+void fixup_perms_recursive(struct dentry *dentry, struct limit_search *limit) {
+       __fixup_perms_recursive(dentry, limit, 0);
 }
 
 void drop_recursive(struct dentry *parent) {