• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 "utility.h"
18 
19 #include <stdint.h>
20 #include <sys/stat.h>
21 #include <sys/sysmacros.h>
22 #include <sys/types.h>
23 #include <sys/vfs.h>
24 #include <unistd.h>
25 
26 #include <android-base/file.h>
27 #include <android-base/logging.h>
28 #include <android-base/stringprintf.h>
29 #include <android-base/strings.h>
30 #include <libfiemap/fiemap_writer.h>
31 
32 namespace android {
33 namespace fiemap {
34 
35 using namespace std::string_literals;
36 using android::base::unique_fd;
37 
38 static constexpr char kUserdataDevice[] = "/dev/block/by-name/userdata";
39 
DetermineMaximumFileSize(const std::string & file_path,uint64_t * result)40 FiemapStatus DetermineMaximumFileSize(const std::string& file_path, uint64_t* result) {
41     // Create the smallest file possible (one block).
42     FiemapUniquePtr writer;
43     auto status = FiemapWriter::Open(file_path, 1, &writer);
44     if (!status.is_ok()) {
45         return status;
46     }
47 
48     *result = 0;
49     switch (writer->fs_type()) {
50         case EXT4_SUPER_MAGIC:
51             // The minimum is 16GiB, so just report that. If we wanted we could parse the
52             // superblock and figure out if 64-bit support is enabled.
53             *result = 17179869184ULL;
54             break;
55         case F2FS_SUPER_MAGIC:
56             // Formula is from https://www.kernel.org/doc/Documentation/filesystems/f2fs.txt
57             // 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB.
58             *result = 4329690886144ULL;
59             break;
60         case MSDOS_SUPER_MAGIC:
61             // 4GB-1, which we want aligned to the block size.
62             *result = 4294967295;
63             *result -= (*result % writer->block_size());
64             break;
65         default:
66             LOG(ERROR) << "Unknown file system type: " << writer->fs_type();
67             break;
68     }
69 
70     // Close and delete the temporary file.
71     writer = nullptr;
72     unlink(file_path.c_str());
73 
74     return FiemapStatus::Ok();
75 }
76 
77 // Given a SplitFiemap, this returns a device path that will work during first-
78 // stage init (i.e., its path can be found by InitRequiredDevices).
GetDevicePathForFile(SplitFiemap * file)79 std::string GetDevicePathForFile(SplitFiemap* file) {
80     auto bdev_path = file->bdev_path();
81 
82     struct stat userdata, given;
83     if (!stat(bdev_path.c_str(), &given) && !stat(kUserdataDevice, &userdata)) {
84         if (S_ISBLK(given.st_mode) && S_ISBLK(userdata.st_mode) &&
85             given.st_rdev == userdata.st_rdev) {
86             return kUserdataDevice;
87         }
88     }
89     return bdev_path;
90 }
91 
JoinPaths(const std::string & dir,const std::string & file)92 std::string JoinPaths(const std::string& dir, const std::string& file) {
93     if (android::base::EndsWith(dir, "/")) {
94         return dir + file;
95     }
96     return dir + "/" + file;
97 }
98 
F2fsPinBeforeAllocate(int file_fd,bool * supported)99 bool F2fsPinBeforeAllocate(int file_fd, bool* supported) {
100     struct stat st;
101     if (fstat(file_fd, &st) < 0) {
102         PLOG(ERROR) << "stat failed";
103         return false;
104     }
105     std::string bdev;
106     if (!BlockDeviceToName(major(st.st_dev), minor(st.st_dev), &bdev)) {
107         LOG(ERROR) << "Failed to get block device name for " << major(st.st_dev) << ":"
108                    << minor(st.st_dev);
109         return false;
110     }
111 
112     std::string contents;
113     std::string feature_file = "/sys/fs/f2fs/" + bdev + "/features";
114     if (!android::base::ReadFileToString(feature_file, &contents)) {
115         PLOG(ERROR) << "read failed: " << feature_file;
116         return false;
117     }
118     contents = android::base::Trim(contents);
119 
120     auto features = android::base::Split(contents, ", ");
121     auto iter = std::find(features.begin(), features.end(), "pin_file"s);
122     *supported = (iter != features.end());
123     return true;
124 }
125 
BlockDeviceToName(uint32_t major,uint32_t minor,std::string * bdev_name)126 bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
127     // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
128     // The directory name in the target corresponds to the name of the block device. We use
129     // that to extract the block device name.
130     // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
131     // follows.
132     //    1:0 -> ../../devices/virtual/block/ram0
133     std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
134     std::string sysfs_bdev;
135 
136     if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
137         PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
138         return false;
139     }
140 
141     *bdev_name = ::android::base::Basename(sysfs_bdev);
142     // Check that the symlink doesn't point to itself.
143     if (sysfs_bdev == *bdev_name) {
144         LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
145         return false;
146     }
147 
148     return true;
149 }
150 
FilesystemHasReliablePinning(const std::string & file,bool * supported)151 bool FilesystemHasReliablePinning(const std::string& file, bool* supported) {
152     struct statfs64 sfs;
153     if (statfs64(file.c_str(), &sfs)) {
154         PLOG(ERROR) << "statfs failed: " << file;
155         return false;
156     }
157     if (sfs.f_type != F2FS_SUPER_MAGIC) {
158         *supported = true;
159         return true;
160     }
161 
162     unique_fd fd(open(file.c_str(), O_RDONLY | O_CLOEXEC));
163     if (fd < 0) {
164         PLOG(ERROR) << "open failed: " << file;
165         return false;
166     }
167     return F2fsPinBeforeAllocate(fd, supported);
168 }
169 
IsSubdir(const std::string & child,const std::string & parent)170 bool IsSubdir(const std::string& child, const std::string& parent) {
171     // Precondition: both are absolute paths.
172     CHECK(android::base::StartsWith(child, "/")) << "Not an absolute path: " << child;
173     CHECK(android::base::StartsWith(parent, "/")) << "Not an absolute path: " << parent;
174 
175     // Remove extraneous "/" at the end.
176     std::string_view child_sv = child;
177     while (child_sv != "/" && android::base::ConsumeSuffix(&child_sv, "/"))
178         ;
179 
180     std::string_view parent_sv = parent;
181     while (parent_sv != "/" && android::base::ConsumeSuffix(&parent_sv, "/"))
182         ;
183 
184     // IsSubdir(anything, "/") => true
185     if (parent_sv == "/") return true;
186 
187     // IsSubdir("/foo", "/foo") => true
188     if (parent_sv == child_sv) return true;
189 
190     // IsSubdir("/foo/bar", "/foo") => true
191     // IsSubdir("/foo-bar", "/foo") => false
192     return android::base::StartsWith(child_sv, std::string(parent_sv) + "/");
193 }
194 
195 }  // namespace fiemap
196 }  // namespace android
197