• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 <private/android_filesystem_config.h>
18 #include <private/canned_fs_config.h>
19 #include <private/fs_config.h>
20 
21 #include <android-base/strings.h>
22 
23 #include <errno.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include <algorithm>
31 #include <fstream>
32 #include <iostream>
33 #include <string>
34 #include <vector>
35 
36 using android::base::ConsumePrefix;
37 using android::base::StartsWith;
38 using android::base::Tokenize;
39 
40 struct Entry {
41     std::string path;
42     unsigned uid;
43     unsigned gid;
44     unsigned mode;
45     uint64_t capabilities;
46 };
47 
48 static std::vector<Entry> canned_data;
49 
load_canned_fs_config(const char * fn)50 int load_canned_fs_config(const char* fn) {
51     std::ifstream input(fn);
52     for (std::string line; std::getline(input, line);) {
53         // Historical: the root dir can be represented as a space character.
54         // e.g. " 1000 1000 0755" is parsed as
55         // path = " ", uid = 1000, gid = 1000, mode = 0755.
56         // But at the same time, we also have accepted
57         // "/ 1000 1000 0755".
58         if (StartsWith(line, " ")) {
59             line.insert(line.begin(), '/');
60         }
61 
62         std::vector<std::string> tokens = Tokenize(line, " ");
63         if (tokens.size() < 4) {
64             std::cerr << "Ill-formed line: " << line << " in " << fn << std::endl;
65             return -1;
66         }
67 
68         // Historical: remove the leading '/' if exists.
69         std::string path(tokens[0].front() == '/' ? std::string(tokens[0], 1) : tokens[0]);
70 
71         Entry e{
72                 .path = std::move(path),
73                 .uid = static_cast<unsigned int>(atoi(tokens[1].c_str())),
74                 .gid = static_cast<unsigned int>(atoi(tokens[2].c_str())),
75                 // mode is in octal
76                 .mode = static_cast<unsigned int>(strtol(tokens[3].c_str(), nullptr, 8)),
77                 .capabilities = 0,
78         };
79 
80         for (size_t i = 4; i < tokens.size(); i++) {
81             std::string_view sv = tokens[i];
82             if (ConsumePrefix(&sv, "capabilities=")) {
83                 e.capabilities = strtoll(std::string(sv).c_str(), nullptr, 0);
84                 break;
85             }
86             // Historical: there can be tokens like "selabel=..." here. They have been ignored.
87             // It's not an error because selabels are applied separately in e2fsdroid using the
88             // file_contexts files set via -S option.
89             std::cerr << "info: ignored token \"" << sv << "\" in " << fn << std::endl;
90         }
91 
92         canned_data.emplace_back(std::move(e));
93     }
94 
95     // Note: we used to sort the entries by path names. This was to improve the lookup performance
96     // by doing binary search. However, this is no longer the case. The lookup performance is not
97     // critical because this tool runs on the host, not on the device. Now, there can be multiple
98     // entries for the same path. Then the one that comes the last wins. This is to allow overriding
99     // platform provided fs_config with a user provided fs_config by appending the latter to the
100     // former.
101     //
102     // To implement the strategy, reverse the entries order, and search from the top.
103     std::reverse(canned_data.begin(), canned_data.end());
104 
105     std::cout << "loaded " << canned_data.size() << " fs_config entries" << std::endl;
106     return 0;
107 }
108 
canned_fs_config(const char * path,int dir,const char * target_out_path,unsigned * uid,unsigned * gid,unsigned * mode,uint64_t * capabilities)109 void canned_fs_config(const char* path, [[maybe_unused]] int dir,
110                       [[maybe_unused]] const char* target_out_path, unsigned* uid, unsigned* gid,
111                       unsigned* mode, uint64_t* capabilities) {
112     if (path != nullptr && path[0] == '/') path++;  // canned paths lack the leading '/'
113 
114     const Entry* found = nullptr;
115     // canned_data is already reversed. First match wins.
116     for (const auto& entry : canned_data) {
117         if (path == entry.path) {
118             found = &entry;
119             break;
120         }
121         continue;
122     }
123 
124     if (found == nullptr) {
125         std::cerr << "failed to find " << path << " in canned fs_config" << std::endl;
126         exit(1);
127     }
128 
129     *uid = found->uid;
130     *gid = found->gid;
131     *mode = found->mode;
132     *capabilities = found->capabilities;
133 }
134