1 //
2 // Copyright (C) 2021 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16
17 #include <string>
18
19 #include <erofs/dir.h>
20
21 #include "update_engine/common/utils.h"
22
23 // The only way to pass extra information to callback function is to use a
24 // wrapper type for erofs_dir_context. So here we go
25 struct erofs_iterate_dir_context {
26 struct erofs_dir_context ctx;
27 std::string path;
28 void* arg;
29 };
30
31 // Dear compiler, please don't reoder fields inside erofs_iterate_dir_context.
32 // Because EROFS expects us to pass a wrapper type. So |ctx| member of
33 // erofs_iterate_dir_context must be put at 0 offset.
34 static_assert(offsetof(erofs_iterate_dir_context, ctx) == 0);
35
36 // Callable shold be a functor like
37 // std::function<int(struct erofs_inode_info *)>
38 template <typename Callable>
erofs_iterate_root_dir(const struct erofs_sb_info * sbi,Callable cb)39 int erofs_iterate_root_dir(const struct erofs_sb_info* sbi, Callable cb) {
40 struct erofs_inode dir {
41 .nid = sbi->root_nid
42 };
43 int err = erofs_read_inode_from_disk(&dir);
44 if (err) {
45 LOG(ERROR) << "Failed to read inode " << sbi->root_nid << " from disk";
46 return err;
47 }
48 struct erofs_iterate_dir_context param {
49 .ctx.dir = &dir, .ctx.pnid = sbi->root_nid,
50 .ctx.cb = [](struct erofs_dir_context* arg) -> int {
51 auto ctx = reinterpret_cast<erofs_iterate_dir_context*>(arg);
52 auto& path = ctx->path;
53 const auto len = path.size();
54 path.push_back('/');
55 path.insert(
56 path.end(), ctx->ctx.dname, ctx->ctx.dname + ctx->ctx.de_namelen);
57 auto cb = static_cast<Callable*>(ctx->arg);
58 const auto err = (*cb)(ctx);
59 if (!err && !ctx->ctx.dot_dotdot && ctx->ctx.de_ftype == EROFS_FT_DIR) {
60 // recursively walk into subdirectories
61 erofs_inode dir{.nid = ctx->ctx.de_nid};
62 if (const int err = erofs_read_inode_from_disk(&dir); err) {
63 return err;
64 }
65 ctx->ctx.dir = &dir;
66 if (const auto err = erofs_iterate_dir(&ctx->ctx, false); err) {
67 return err;
68 }
69 }
70 path.resize(len);
71 return err;
72 },
73 .arg = &cb,
74 };
75 return erofs_iterate_dir(¶m.ctx, false);
76 }
77