• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 <libfiemap_writer/fiemap_writer.h>
18 
19 #include <dirent.h>
20 #include <fcntl.h>
21 #include <linux/fs.h>
22 #include <stdio.h>
23 #include <sys/ioctl.h>
24 #include <sys/stat.h>
25 #include <sys/sysmacros.h>
26 #include <sys/types.h>
27 #include <sys/vfs.h>
28 #include <unistd.h>
29 
30 #include <limits>
31 #include <string>
32 #include <utility>
33 #include <vector>
34 
35 #include <android-base/file.h>
36 #include <android-base/logging.h>
37 #include <android-base/stringprintf.h>
38 #include <android-base/strings.h>
39 #include <android-base/unique_fd.h>
40 
41 namespace android {
42 namespace fiemap_writer {
43 
44 // We are expecting no more than 512 extents in a fiemap of the file we create.
45 // If we find more, then it is treated as error for now.
46 static constexpr const uint32_t kMaxExtents = 512;
47 
48 // TODO: Fallback to using fibmap if FIEMAP_EXTENT_MERGED is set.
49 static constexpr const uint32_t kUnsupportedExtentFlags =
50         FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_DELALLOC |
51         FIEMAP_EXTENT_NOT_ALIGNED | FIEMAP_EXTENT_DATA_INLINE | FIEMAP_EXTENT_DATA_TAIL |
52         FIEMAP_EXTENT_UNWRITTEN | FIEMAP_EXTENT_SHARED | FIEMAP_EXTENT_MERGED;
53 
54 // Large file support must be enabled.
55 static_assert(sizeof(off_t) == sizeof(uint64_t));
56 
cleanup(const std::string & file_path,bool created)57 static inline void cleanup(const std::string& file_path, bool created) {
58     if (created) {
59         unlink(file_path.c_str());
60     }
61 }
62 
BlockDeviceToName(uint32_t major,uint32_t minor,std::string * bdev_name)63 static bool BlockDeviceToName(uint32_t major, uint32_t minor, std::string* bdev_name) {
64     // The symlinks in /sys/dev/block point to the block device node under /sys/device/..
65     // The directory name in the target corresponds to the name of the block device. We use
66     // that to extract the block device name.
67     // e.g for block device name 'ram0', there exists a symlink named '1:0' in /sys/dev/block as
68     // follows.
69     //    1:0 -> ../../devices/virtual/block/ram0
70     std::string sysfs_path = ::android::base::StringPrintf("/sys/dev/block/%u:%u", major, minor);
71     std::string sysfs_bdev;
72 
73     if (!::android::base::Readlink(sysfs_path, &sysfs_bdev)) {
74         PLOG(ERROR) << "Failed to read link at: " << sysfs_path;
75         return false;
76     }
77 
78     *bdev_name = ::android::base::Basename(sysfs_bdev);
79     // Paranoid sanity check to make sure we just didn't get the
80     // input in return as-is.
81     if (sysfs_bdev == *bdev_name) {
82         LOG(ERROR) << "Malformed symlink for block device: " << sysfs_bdev;
83         return false;
84     }
85 
86     return true;
87 }
88 
DeviceMapperStackPop(const std::string & bdev,std::string * bdev_raw)89 static bool DeviceMapperStackPop(const std::string& bdev, std::string* bdev_raw) {
90     // TODO: Stop popping the device mapper stack if dm-linear target is found
91     if (!::android::base::StartsWith(bdev, "dm-")) {
92         // We are at the bottom of the device mapper stack.
93         *bdev_raw = bdev;
94         return true;
95     }
96 
97     std::string dm_leaf_dir = ::android::base::StringPrintf("/sys/block/%s/slaves", bdev.c_str());
98     auto d = std::unique_ptr<DIR, decltype(&closedir)>(opendir(dm_leaf_dir.c_str()), closedir);
99     if (d == nullptr) {
100         PLOG(ERROR) << "Failed to open: " << dm_leaf_dir;
101         return false;
102     }
103 
104     struct dirent* de;
105     uint32_t num_leaves = 0;
106     std::string bdev_next = "";
107     while ((de = readdir(d.get())) != nullptr) {
108         if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
109             continue;
110         }
111 
112         // We set the first name we find here
113         if (bdev_next.empty()) {
114             bdev_next = de->d_name;
115         }
116         num_leaves++;
117     }
118 
119     // if we have more than one leaves, we return immediately. We can't continue to create the
120     // file since we don't know how to write it out using fiemap, so it will be readable via the
121     // underlying block devices later. The reader will also have to construct the same device mapper
122     // target in order read the file out.
123     if (num_leaves > 1) {
124         LOG(ERROR) << "Found " << num_leaves << " leaf block devices under device mapper device "
125                    << bdev;
126         return false;
127     }
128 
129     // recursively call with the block device we found in order to pop the device mapper stack.
130     return DeviceMapperStackPop(bdev_next, bdev_raw);
131 }
132 
GetBlockDeviceForFile(const std::string & file_path,std::string * bdev_path,bool * uses_dm)133 bool FiemapWriter::GetBlockDeviceForFile(const std::string& file_path, std::string* bdev_path,
134                                          bool* uses_dm) {
135     struct stat sb;
136     if (stat(file_path.c_str(), &sb)) {
137         PLOG(ERROR) << "Failed to get stat for: " << file_path;
138         return false;
139     }
140 
141     std::string bdev;
142     if (!BlockDeviceToName(major(sb.st_dev), minor(sb.st_dev), &bdev)) {
143         LOG(ERROR) << "Failed to get block device name for " << major(sb.st_dev) << ":"
144                    << minor(sb.st_dev);
145         return false;
146     }
147 
148     std::string bdev_raw;
149     if (!DeviceMapperStackPop(bdev, &bdev_raw)) {
150         LOG(ERROR) << "Failed to get the bottom of the device mapper stack for device: " << bdev;
151         return false;
152     }
153 
154     if (uses_dm) {
155         *uses_dm = (bdev_raw != bdev);
156     }
157 
158     LOG(DEBUG) << "Popped device (" << bdev_raw << ") from device mapper stack starting with ("
159                << bdev << ")";
160 
161     *bdev_path = ::android::base::StringPrintf("/dev/block/%s", bdev_raw.c_str());
162 
163     // Make sure we are talking to a block device before calling it a success.
164     if (stat(bdev_path->c_str(), &sb)) {
165         PLOG(ERROR) << "Failed to get stat for block device: " << *bdev_path;
166         return false;
167     }
168 
169     if ((sb.st_mode & S_IFMT) != S_IFBLK) {
170         PLOG(ERROR) << "File: " << *bdev_path << " is not a block device";
171         return false;
172     }
173 
174     return true;
175 }
176 
GetBlockDeviceSize(int bdev_fd,const std::string & bdev_path,uint64_t * bdev_size)177 static bool GetBlockDeviceSize(int bdev_fd, const std::string& bdev_path, uint64_t* bdev_size) {
178     uint64_t size_in_bytes = 0;
179     if (ioctl(bdev_fd, BLKGETSIZE64, &size_in_bytes)) {
180         PLOG(ERROR) << "Failed to get total size for: " << bdev_path;
181         return false;
182     }
183 
184     *bdev_size = size_in_bytes;
185 
186     return true;
187 }
188 
GetFileSize(const std::string & file_path)189 static uint64_t GetFileSize(const std::string& file_path) {
190     struct stat sb;
191     if (stat(file_path.c_str(), &sb)) {
192         PLOG(ERROR) << "Failed to get size for file: " << file_path;
193         return 0;
194     }
195 
196     return sb.st_size;
197 }
198 
PerformFileChecks(const std::string & file_path,uint64_t file_size,uint64_t * blocksz,uint32_t * fs_type)199 static bool PerformFileChecks(const std::string& file_path, uint64_t file_size, uint64_t* blocksz,
200                               uint32_t* fs_type) {
201     struct statfs64 sfs;
202     if (statfs64(file_path.c_str(), &sfs)) {
203         PLOG(ERROR) << "Failed to read file system status at: " << file_path;
204         return false;
205     }
206 
207     if (!sfs.f_bsize) {
208         LOG(ERROR) << "Unsupported block size: " << sfs.f_bsize;
209         return false;
210     }
211 
212     // Check if the filesystem is of supported types.
213     // Only ext4, f2fs, and vfat are tested and supported.
214     switch (sfs.f_type) {
215         case EXT4_SUPER_MAGIC:
216         case F2FS_SUPER_MAGIC:
217         case MSDOS_SUPER_MAGIC:
218             break;
219         default:
220             LOG(ERROR) << "Unsupported file system type: 0x" << std::hex << sfs.f_type;
221             return false;
222     }
223 
224     uint64_t available_bytes = sfs.f_bsize * sfs.f_bavail;
225     if (available_bytes <= file_size) {
226         LOG(ERROR) << "Not enough free space in file system to create file of size : " << file_size;
227         return false;
228     }
229 
230     *blocksz = sfs.f_bsize;
231     *fs_type = sfs.f_type;
232     return true;
233 }
234 
FallocateFallback(int file_fd,uint64_t block_size,uint64_t file_size,const std::string & file_path,const std::function<bool (uint64_t,uint64_t)> & on_progress)235 static bool FallocateFallback(int file_fd, uint64_t block_size, uint64_t file_size,
236                               const std::string& file_path,
237                               const std::function<bool(uint64_t, uint64_t)>& on_progress) {
238     // Even though this is much faster than writing zeroes, it is still slow
239     // enough that we need to fire the progress callback periodically. To
240     // easily achieve this, we seek in chunks. We use 1000 chunks since
241     // normally we only fire the callback on 1/1000th increments.
242     uint64_t bytes_per_chunk = std::max(file_size / 1000, block_size);
243 
244     // Seek just to the end of each chunk and write a single byte, causing
245     // the filesystem to allocate blocks.
246     off_t cursor = 0;
247     off_t end = static_cast<off_t>(file_size);
248     while (cursor < end) {
249         cursor = std::min(static_cast<off_t>(cursor + bytes_per_chunk), end);
250         auto rv = TEMP_FAILURE_RETRY(lseek(file_fd, cursor - 1, SEEK_SET));
251         if (rv < 0) {
252             PLOG(ERROR) << "Failed to lseek " << file_path;
253             return false;
254         }
255         if (rv != cursor - 1) {
256             LOG(ERROR) << "Seek returned wrong offset " << rv << " for file " << file_path;
257             return false;
258         }
259         char buffer[] = {0};
260         if (!android::base::WriteFully(file_fd, buffer, 1)) {
261             PLOG(ERROR) << "Write failed: " << file_path;
262             return false;
263         }
264         if (on_progress && !on_progress(cursor, file_size)) {
265             return false;
266         }
267     }
268     return true;
269 }
270 
AllocateFile(int file_fd,const std::string & file_path,uint64_t blocksz,uint64_t file_size,unsigned int fs_type,std::function<bool (uint64_t,uint64_t)> on_progress)271 static bool AllocateFile(int file_fd, const std::string& file_path, uint64_t blocksz,
272                          uint64_t file_size, unsigned int fs_type,
273                          std::function<bool(uint64_t, uint64_t)> on_progress) {
274     // Reserve space for the file on the file system and write it out to make sure the extents
275     // don't come back unwritten. Return from this function with the kernel file offset set to 0.
276     // If the filesystem is f2fs, then we also PIN the file on disk to make sure the blocks
277     // aren't moved around.
278     switch (fs_type) {
279         case EXT4_SUPER_MAGIC:
280         case F2FS_SUPER_MAGIC:
281             if (fallocate(file_fd, FALLOC_FL_ZERO_RANGE, 0, file_size)) {
282                 PLOG(ERROR) << "Failed to allocate space for file: " << file_path
283                             << " size: " << file_size;
284                 return false;
285             }
286             break;
287         case MSDOS_SUPER_MAGIC:
288             // fallocate() is not supported, and not needed, since VFAT does not support holes.
289             // Instead we can perform a much faster allocation.
290             return FallocateFallback(file_fd, blocksz, file_size, file_path, on_progress);
291         default:
292             LOG(ERROR) << "Missing fallocate() support for file system " << fs_type;
293             return false;
294     }
295 
296     // write zeroes in 'blocksz' byte increments until we reach file_size to make sure the data
297     // blocks are actually written to by the file system and thus getting rid of the holes in the
298     // file.
299     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, blocksz), free);
300     if (buffer == nullptr) {
301         LOG(ERROR) << "failed to allocate memory for writing file";
302         return false;
303     }
304 
305     off64_t offset = lseek64(file_fd, 0, SEEK_SET);
306     if (offset < 0) {
307         PLOG(ERROR) << "Failed to seek at the beginning of : " << file_path;
308         return false;
309     }
310 
311     int permille = -1;
312     while (offset < file_size) {
313         if (!::android::base::WriteFully(file_fd, buffer.get(), blocksz)) {
314             PLOG(ERROR) << "Failed to write" << blocksz << " bytes at offset" << offset
315                         << " in file " << file_path;
316             return false;
317         }
318 
319         offset += blocksz;
320 
321         // Don't invoke the callback every iteration - wait until a significant
322         // chunk (here, 1/1000th) of the data has been processed.
323         int new_permille = (static_cast<uint64_t>(offset) * 1000) / file_size;
324         if (new_permille != permille && static_cast<uint64_t>(offset) != file_size) {
325             if (on_progress && !on_progress(offset, file_size)) {
326                 return false;
327             }
328             permille = new_permille;
329         }
330     }
331 
332     if (lseek64(file_fd, 0, SEEK_SET) < 0) {
333         PLOG(ERROR) << "Failed to reset offset at the beginning of : " << file_path;
334         return false;
335     }
336 
337     // flush all writes here ..
338     if (fsync(file_fd)) {
339         PLOG(ERROR) << "Failed to synchronize written file:" << file_path;
340         return false;
341     }
342 
343     // Send one last progress notification.
344     if (on_progress && !on_progress(file_size, file_size)) {
345         return false;
346     }
347     return true;
348 }
349 
PinFile(int file_fd,const std::string & file_path,uint32_t fs_type)350 static bool PinFile(int file_fd, const std::string& file_path, uint32_t fs_type) {
351     if (fs_type != F2FS_SUPER_MAGIC) {
352         // No pinning necessary for ext4/msdos. The blocks, once allocated, are
353         // expected to be fixed.
354         return true;
355     }
356 
357 // F2FS-specific ioctl
358 // It requires the below kernel commit merged in v4.16-rc1.
359 //   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
360 // In android-4.4,
361 //   56ee1e817908 ("f2fs: updates on v4.16-rc1")
362 // In android-4.9,
363 //   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
364 // In android-4.14,
365 //   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
366 #ifndef F2FS_IOC_SET_PIN_FILE
367 #ifndef F2FS_IOCTL_MAGIC
368 #define F2FS_IOCTL_MAGIC 0xf5
369 #endif
370 #define F2FS_IOC_SET_PIN_FILE _IOW(F2FS_IOCTL_MAGIC, 13, __u32)
371 #endif
372 
373     uint32_t pin_status = 1;
374     int error = ioctl(file_fd, F2FS_IOC_SET_PIN_FILE, &pin_status);
375     if (error < 0) {
376         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
377             PLOG(ERROR) << "Failed to pin file, not supported by kernel: " << file_path;
378         } else {
379             PLOG(ERROR) << "Failed to pin file: " << file_path;
380         }
381         return false;
382     }
383 
384     return true;
385 }
386 
IsFilePinned(int file_fd,const std::string & file_path,uint32_t fs_type)387 static bool IsFilePinned(int file_fd, const std::string& file_path, uint32_t fs_type) {
388     if (fs_type != F2FS_SUPER_MAGIC) {
389         // No pinning necessary for ext4 or vfat. The blocks, once allocated,
390         // are expected to be fixed.
391         return true;
392     }
393 
394 // F2FS-specific ioctl
395 // It requires the below kernel commit merged in v4.16-rc1.
396 //   1ad71a27124c ("f2fs: add an ioctl to disable GC for specific file")
397 // In android-4.4,
398 //   56ee1e817908 ("f2fs: updates on v4.16-rc1")
399 // In android-4.9,
400 //   2f17e34672a8 ("f2fs: updates on v4.16-rc1")
401 // In android-4.14,
402 //   ce767d9a55bc ("f2fs: updates on v4.16-rc1")
403 #ifndef F2FS_IOC_GET_PIN_FILE
404 #ifndef F2FS_IOCTL_MAGIC
405 #define F2FS_IOCTL_MAGIC 0xf5
406 #endif
407 #define F2FS_IOC_GET_PIN_FILE _IOR(F2FS_IOCTL_MAGIC, 14, __u32)
408 #endif
409 
410     // f2fs: export FS_NOCOW_FL flag to user
411     uint32_t flags;
412     int error = ioctl(file_fd, FS_IOC_GETFLAGS, &flags);
413     if (error < 0) {
414         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
415             PLOG(ERROR) << "Failed to get flags, not supported by kernel: " << file_path;
416         } else {
417             PLOG(ERROR) << "Failed to get flags: " << file_path;
418         }
419         return false;
420     }
421     if (!(flags & FS_NOCOW_FL)) {
422         LOG(ERROR) << "It is not pinned: " << file_path;
423         return false;
424     }
425 
426     // F2FS_IOC_GET_PIN_FILE returns the number of blocks moved.
427     uint32_t moved_blocks_nr;
428     error = ioctl(file_fd, F2FS_IOC_GET_PIN_FILE, &moved_blocks_nr);
429     if (error < 0) {
430         if ((errno == ENOTTY) || (errno == ENOTSUP)) {
431             PLOG(ERROR) << "Failed to get file pin status, not supported by kernel: " << file_path;
432         } else {
433             PLOG(ERROR) << "Failed to get file pin status: " << file_path;
434         }
435         return false;
436     }
437 
438     if (moved_blocks_nr) {
439         LOG(ERROR) << moved_blocks_nr << " blocks moved in file " << file_path;
440     }
441     return moved_blocks_nr == 0;
442 }
443 
HasPinnedExtents(const std::string & file_path)444 bool FiemapWriter::HasPinnedExtents(const std::string& file_path) {
445     android::base::unique_fd fd(open(file_path.c_str(), O_NOFOLLOW | O_CLOEXEC | O_RDONLY));
446     if (fd < 0) {
447         PLOG(ERROR) << "open: " << file_path;
448         return false;
449     }
450 
451     struct statfs64 sfs;
452     if (fstatfs64(fd, &sfs)) {
453         PLOG(ERROR) << "fstatfs64: " << file_path;
454         return false;
455     }
456     return IsFilePinned(fd, file_path, sfs.f_type);
457 }
458 
ReadFiemap(int file_fd,const std::string & file_path,std::vector<struct fiemap_extent> * extents)459 static bool ReadFiemap(int file_fd, const std::string& file_path,
460                        std::vector<struct fiemap_extent>* extents) {
461     uint64_t fiemap_size =
462             sizeof(struct fiemap_extent) + kMaxExtents * sizeof(struct fiemap_extent);
463     auto buffer = std::unique_ptr<void, decltype(&free)>(calloc(1, fiemap_size), free);
464     if (buffer == nullptr) {
465         LOG(ERROR) << "Failed to allocate memory for fiemap";
466         return false;
467     }
468 
469     struct fiemap* fiemap = reinterpret_cast<struct fiemap*>(buffer.get());
470     fiemap->fm_start = 0;
471     fiemap->fm_length = UINT64_MAX;
472     // make sure file is synced to disk before we read the fiemap
473     fiemap->fm_flags = FIEMAP_FLAG_SYNC;
474     fiemap->fm_extent_count = kMaxExtents;
475 
476     if (ioctl(file_fd, FS_IOC_FIEMAP, fiemap)) {
477         PLOG(ERROR) << "Failed to get FIEMAP from the kernel for file: " << file_path;
478         return false;
479     }
480 
481     if (fiemap->fm_mapped_extents == 0) {
482         LOG(ERROR) << "File " << file_path << " has zero extents";
483         return false;
484     }
485 
486     // Iterate through each extent read and make sure its valid before adding it to the vector
487     bool last_extent_seen = false;
488     struct fiemap_extent* extent = &fiemap->fm_extents[0];
489     for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++, extent++) {
490         // LogExtent(i + 1, *extent);
491         if (extent->fe_flags & kUnsupportedExtentFlags) {
492             LOG(ERROR) << "Extent " << i + 1 << " of file " << file_path
493                        << " has unsupported flags";
494             extents->clear();
495             return false;
496         }
497 
498         if (extent->fe_flags & FIEMAP_EXTENT_LAST) {
499             last_extent_seen = true;
500             if (i != (fiemap->fm_mapped_extents - 1)) {
501                 LOG(WARNING) << "Extents are being received out-of-order";
502             }
503         }
504         extents->emplace_back(std::move(*extent));
505     }
506 
507     if (!last_extent_seen) {
508         // The file is possibly too fragmented.
509         if (fiemap->fm_mapped_extents == kMaxExtents) {
510             LOG(ERROR) << "File is too fragmented, needs more than " << kMaxExtents << " extents.";
511         }
512         extents->clear();
513     }
514 
515     return last_extent_seen;
516 }
517 
ReadFibmap(int file_fd,const std::string & file_path,std::vector<struct fiemap_extent> * extents)518 static bool ReadFibmap(int file_fd, const std::string& file_path,
519                        std::vector<struct fiemap_extent>* extents) {
520     struct stat s;
521     if (fstat(file_fd, &s)) {
522         PLOG(ERROR) << "Failed to stat " << file_path;
523         return false;
524     }
525 
526     unsigned int blksize;
527     if (ioctl(file_fd, FIGETBSZ, &blksize) < 0) {
528         PLOG(ERROR) << "Failed to get FIGETBSZ for " << file_path;
529         return false;
530     }
531     if (!blksize) {
532         LOG(ERROR) << "Invalid filesystem block size: " << blksize;
533         return false;
534     }
535 
536     uint64_t num_blocks = (s.st_size + blksize - 1) / blksize;
537     if (num_blocks > std::numeric_limits<uint32_t>::max()) {
538         LOG(ERROR) << "Too many blocks for FIBMAP (" << num_blocks << ")";
539         return false;
540     }
541 
542     for (uint32_t last_block, block_number = 0; block_number < num_blocks; block_number++) {
543         uint32_t block = block_number;
544         if (ioctl(file_fd, FIBMAP, &block)) {
545             PLOG(ERROR) << "Failed to get FIBMAP for file " << file_path;
546             return false;
547         }
548         if (!block) {
549             LOG(ERROR) << "Logical block " << block_number << " is a hole, which is not supported";
550             return false;
551         }
552 
553         if (!extents->empty() && block == last_block + 1) {
554             extents->back().fe_length += blksize;
555         } else {
556             extents->push_back(fiemap_extent{.fe_logical = block_number,
557                                              .fe_physical = static_cast<uint64_t>(block) * blksize,
558                                              .fe_length = static_cast<uint64_t>(blksize),
559                                              .fe_flags = 0});
560         }
561         last_block = block;
562     }
563     return true;
564 }
565 
Open(const std::string & file_path,uint64_t file_size,bool create,std::function<bool (uint64_t,uint64_t)> progress)566 FiemapUniquePtr FiemapWriter::Open(const std::string& file_path, uint64_t file_size, bool create,
567                                    std::function<bool(uint64_t, uint64_t)> progress) {
568     // if 'create' is false, open an existing file and do not truncate.
569     int open_flags = O_RDWR | O_CLOEXEC;
570     if (create) {
571         if (access(file_path.c_str(), F_OK) == 0) {
572             LOG(WARNING) << "File " << file_path << " already exists, truncating";
573         }
574         open_flags |= O_CREAT | O_TRUNC;
575     }
576     ::android::base::unique_fd file_fd(
577             TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags, S_IRUSR | S_IWUSR)));
578     if (file_fd < 0) {
579         PLOG(ERROR) << "Failed to create file at: " << file_path;
580         return nullptr;
581     }
582 
583     std::string abs_path;
584     if (!::android::base::Realpath(file_path, &abs_path)) {
585         PLOG(ERROR) << "Invalid file path: " << file_path;
586         cleanup(file_path, create);
587         return nullptr;
588     }
589 
590     std::string bdev_path;
591     if (!GetBlockDeviceForFile(abs_path, &bdev_path)) {
592         LOG(ERROR) << "Failed to get block dev path for file: " << file_path;
593         cleanup(abs_path, create);
594         return nullptr;
595     }
596 
597     ::android::base::unique_fd bdev_fd(
598             TEMP_FAILURE_RETRY(open(bdev_path.c_str(), O_RDONLY | O_CLOEXEC)));
599     if (bdev_fd < 0) {
600         PLOG(ERROR) << "Failed to open block device: " << bdev_path;
601         cleanup(file_path, create);
602         return nullptr;
603     }
604 
605     uint64_t bdevsz;
606     if (!GetBlockDeviceSize(bdev_fd, bdev_path, &bdevsz)) {
607         LOG(ERROR) << "Failed to get block device size for : " << bdev_path;
608         cleanup(file_path, create);
609         return nullptr;
610     }
611 
612     if (!create) {
613         file_size = GetFileSize(abs_path);
614         if (file_size == 0) {
615             LOG(ERROR) << "Invalid file size of zero bytes for file: " << abs_path;
616             return nullptr;
617         }
618     }
619 
620     uint64_t blocksz;
621     uint32_t fs_type;
622     if (!PerformFileChecks(abs_path, file_size, &blocksz, &fs_type)) {
623         LOG(ERROR) << "Failed to validate file or file system for file:" << abs_path;
624         cleanup(abs_path, create);
625         return nullptr;
626     }
627 
628     // Align up to the nearest block size.
629     if (file_size % blocksz) {
630         file_size += blocksz - (file_size % blocksz);
631     }
632 
633     if (create) {
634         if (!AllocateFile(file_fd, abs_path, blocksz, file_size, fs_type, std::move(progress))) {
635             LOG(ERROR) << "Failed to allocate file: " << abs_path << " of size: " << file_size
636                        << " bytes";
637             cleanup(abs_path, create);
638             return nullptr;
639         }
640     }
641 
642     // f2fs may move the file blocks around.
643     if (!PinFile(file_fd, abs_path, fs_type)) {
644         cleanup(abs_path, create);
645         LOG(ERROR) << "Failed to pin the file in storage";
646         return nullptr;
647     }
648 
649     // now allocate the FiemapWriter and start setting it up
650     FiemapUniquePtr fmap(new FiemapWriter());
651     switch (fs_type) {
652         case EXT4_SUPER_MAGIC:
653         case F2FS_SUPER_MAGIC:
654             if (!ReadFiemap(file_fd, abs_path, &fmap->extents_)) {
655                 LOG(ERROR) << "Failed to read fiemap of file: " << abs_path;
656                 cleanup(abs_path, create);
657                 return nullptr;
658             }
659             break;
660         case MSDOS_SUPER_MAGIC:
661             if (!ReadFibmap(file_fd, abs_path, &fmap->extents_)) {
662                 LOG(ERROR) << "Failed to read fibmap of file: " << abs_path;
663                 cleanup(abs_path, create);
664                 return nullptr;
665             }
666             break;
667     }
668 
669     fmap->file_path_ = abs_path;
670     fmap->bdev_path_ = bdev_path;
671     fmap->file_size_ = file_size;
672     fmap->bdev_size_ = bdevsz;
673     fmap->fs_type_ = fs_type;
674     fmap->block_size_ = blocksz;
675 
676     LOG(VERBOSE) << "Successfully created FiemapWriter for file " << abs_path << " on block device "
677                  << bdev_path;
678     return fmap;
679 }
680 
681 }  // namespace fiemap_writer
682 }  // namespace android
683