summaryrefslogtreecommitdiff
path: root/jni/node.cpp
blob: 25f732d38a0a5c5af2509b2398075dc30a85fcbf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specic language governing permissions and
 * limitations under the License.
 */

#include "node-inl.h"

static std::vector<std::string> GetPathSegments(int segment_start, const std::string& path) {
    std::vector<std::string> segments;
    int segment_end = path.find_first_of('/', segment_start);

    while (segment_end != std::string::npos) {
        if (segment_end == segment_start) {
            // First character is '/' ignore
            segment_end = path.find_first_of('/', ++segment_start);
            continue;
        }

        segments.push_back(path.substr(segment_start, segment_end - segment_start));
        segment_start = segment_end + 1;
        segment_end = path.find_first_of('/', segment_start);
    }
    if (segment_start < path.size()) {
        segments.push_back(path.substr(segment_start));
    }
    return segments;
}

namespace mediaprovider {
namespace fuse {

// Assumes that |node| has at least one child.
void node::BuildPathForNodeRecursive(bool safe, const node* node, std::stringstream* path) const {
    if (node->parent_) {
        BuildPathForNodeRecursive(safe, node->parent_, path);
    }

    if (safe && node->parent_) {
        (*path) << reinterpret_cast<uintptr_t>(node);
    } else {
        (*path) << node->GetName();
    }
  
    if (node != this) {
        // Must not add a '/' to the last segment
        (*path) << "/";
    }
}

std::string node::BuildPath() const {
    std::lock_guard<std::recursive_mutex> guard(*lock_);
    std::stringstream path;

    BuildPathForNodeRecursive(false, this, &path);
    return path.str();
}

std::string node::BuildSafePath() const {
    std::lock_guard<std::recursive_mutex> guard(*lock_);
    std::stringstream path;

    BuildPathForNodeRecursive(true, this, &path);
    return path.str();
}

const node* node::LookupAbsolutePath(const node* root, const std::string& absolute_path) {
    if (absolute_path.find(root->GetName()) != 0) {
        return nullptr;
    }

    std::vector<std::string> segments = GetPathSegments(root->GetName().size(), absolute_path);

    std::lock_guard<std::recursive_mutex> guard(*root->lock_);

    const node* node = root;
    for (const std::string& segment : segments) {
        node = node->LookupChildByName(segment, false /* acquire */);
        if (!node) {
            return nullptr;
        }
    }
    return node;
}

const node* node::LookupInode(const node* root, ino_t ino) {
    CHECK(root);

    std::lock_guard<std::recursive_mutex> guard(*root->lock_);

    if ((root->ino_ == ino) && !root->deleted_ && !(root->handles_.empty())) {
        return root;
    }

    for (node* child : root->children_) {
        const node* node = LookupInode(child, ino);
        if (node) {
            return node;
        }
    }
    return nullptr;
}

void node::DeleteTree(node* tree) {
    std::lock_guard<std::recursive_mutex> guard(*tree->lock_);

    if (tree) {
        // Guarantee this node not be released while deleting its children.
        // pf_forget could be called for a parent node first not its children
        // when evicting file system inodes by shrinker, so the parent node
        // could exist without its own reference but having a children node.
        // In this case, this node could be deleted during executing
        // DeleteTree(child), and it causes double free for the node.
        tree->Acquire();

        // Make a copy of the list of children because calling Delete tree
        // will modify the list of children, which will cause issues while
        // iterating over them.
        std::vector<node*> children(tree->children_.begin(), tree->children_.end());
        for (node* child : children) {
            DeleteTree(child);
        }

        CHECK(tree->children_.empty());
        delete tree;
    }
}

}  // namespace fuse
}  // namespace mediaprovider