Home | History | Annotate | Download | only in vold
      1 /*
      2  * Copyright (C) 2015 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 <memory>
     18 #include <string>
     19 #include <vector>
     20 
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <errno.h>
     24 #include <sys/types.h>
     25 #include <sys/stat.h>
     26 #include <fcntl.h>
     27 #include <linux/fs.h>
     28 #include <linux/fiemap.h>
     29 #include <mntent.h>
     30 
     31 #include <android-base/logging.h>
     32 
     33 #include <AutoCloseFD.h>
     34 
     35 namespace {
     36 
     37 struct Options {
     38     std::vector<std::string> targets;
     39     bool unlink{true};
     40 };
     41 
     42 constexpr uint32_t max_extents = 32;
     43 
     44 bool read_command_line(int argc, const char * const argv[], Options &options);
     45 void usage(const char *progname);
     46 bool secdiscard_path(const std::string &path);
     47 std::unique_ptr<struct fiemap> path_fiemap(const std::string &path, uint32_t extent_count);
     48 bool check_fiemap(const struct fiemap &fiemap, const std::string &path);
     49 std::unique_ptr<struct fiemap> alloc_fiemap(uint32_t extent_count);
     50 std::string block_device_for_path(const std::string &path);
     51 bool overwrite_with_zeros(int fd, off64_t start, off64_t length);
     52 
     53 }
     54 
     55 int main(int argc, const char * const argv[]) {
     56     android::base::InitLogging(const_cast<char **>(argv));
     57     Options options;
     58     if (!read_command_line(argc, argv, options)) {
     59         usage(argv[0]);
     60         return -1;
     61     }
     62     for (auto const &target: options.targets) {
     63         LOG(DEBUG) << "Securely discarding '" << target << "' unlink=" << options.unlink;
     64         if (!secdiscard_path(target)) {
     65             LOG(ERROR) << "Secure discard failed for: " << target;
     66         }
     67         if (options.unlink) {
     68             if (unlink(target.c_str()) != 0 && errno != ENOENT) {
     69                 PLOG(ERROR) << "Unable to unlink: " << target;
     70             }
     71         }
     72         LOG(DEBUG) << "Discarded: " << target;
     73     }
     74     return 0;
     75 }
     76 
     77 namespace {
     78 
     79 bool read_command_line(int argc, const char * const argv[], Options &options) {
     80     for (int i = 1; i < argc; i++) {
     81         if (!strcmp("--no-unlink", argv[i])) {
     82             options.unlink = false;
     83         } else if (!strcmp("--", argv[i])) {
     84             for (int j = i+1; j < argc; j++) {
     85                 if (argv[j][0] != '/') return false; // Must be absolute path
     86                 options.targets.emplace_back(argv[j]);
     87             }
     88             return options.targets.size() > 0;
     89         } else {
     90             return false; // Unknown option
     91         }
     92     }
     93     return false; // "--" not found
     94 }
     95 
     96 void usage(const char *progname) {
     97     fprintf(stderr, "Usage: %s [--no-unlink] -- <absolute path> ...\n", progname);
     98 }
     99 
    100 // BLKSECDISCARD all content in "path", if it's small enough.
    101 bool secdiscard_path(const std::string &path) {
    102     auto fiemap = path_fiemap(path, max_extents);
    103     if (!fiemap || !check_fiemap(*fiemap, path)) {
    104         return false;
    105     }
    106     auto block_device = block_device_for_path(path);
    107     if (block_device.empty()) {
    108         return false;
    109     }
    110     AutoCloseFD fs_fd(block_device, O_RDWR | O_LARGEFILE);
    111     if (!fs_fd) {
    112         PLOG(ERROR) << "Failed to open device " << block_device;
    113         return false;
    114     }
    115     for (uint32_t i = 0; i < fiemap->fm_mapped_extents; i++) {
    116         uint64_t range[2];
    117         range[0] = fiemap->fm_extents[i].fe_physical;
    118         range[1] = fiemap->fm_extents[i].fe_length;
    119         if (ioctl(fs_fd.get(), BLKSECDISCARD, range) == -1) {
    120             PLOG(ERROR) << "Unable to BLKSECDISCARD " << path;
    121             if (!overwrite_with_zeros(fs_fd.get(), range[0], range[1])) return false;
    122             LOG(DEBUG) << "Used zero overwrite";
    123         }
    124     }
    125     return true;
    126 }
    127 
    128 // Read the file's FIEMAP
    129 std::unique_ptr<struct fiemap> path_fiemap(const std::string &path, uint32_t extent_count)
    130 {
    131     AutoCloseFD fd(path);
    132     if (!fd) {
    133         if (errno == ENOENT) {
    134             PLOG(DEBUG) << "Unable to open " << path;
    135         } else {
    136             PLOG(ERROR) << "Unable to open " << path;
    137         }
    138         return nullptr;
    139     }
    140     auto fiemap = alloc_fiemap(extent_count);
    141     if (ioctl(fd.get(), FS_IOC_FIEMAP, fiemap.get()) != 0) {
    142         PLOG(ERROR) << "Unable to FIEMAP " << path;
    143         return nullptr;
    144     }
    145     auto mapped = fiemap->fm_mapped_extents;
    146     if (mapped < 1 || mapped > extent_count) {
    147         LOG(ERROR) << "Extent count not in bounds 1 <= " << mapped << " <= " << extent_count
    148             << " in " << path;
    149         return nullptr;
    150     }
    151     return fiemap;
    152 }
    153 
    154 // Ensure that the FIEMAP covers the file and is OK to discard
    155 bool check_fiemap(const struct fiemap &fiemap, const std::string &path) {
    156     auto mapped = fiemap.fm_mapped_extents;
    157     if (!(fiemap.fm_extents[mapped - 1].fe_flags & FIEMAP_EXTENT_LAST)) {
    158         LOG(ERROR) << "Extent " << mapped -1 << " was not the last in " << path;
    159         return false;
    160     }
    161     for (uint32_t i = 0; i < mapped; i++) {
    162         auto flags = fiemap.fm_extents[i].fe_flags;
    163         if (flags & (FIEMAP_EXTENT_UNKNOWN | FIEMAP_EXTENT_DELALLOC | FIEMAP_EXTENT_NOT_ALIGNED)) {
    164             LOG(ERROR) << "Extent " << i << " has unexpected flags " << flags << ": " << path;
    165             return false;
    166         }
    167     }
    168     return true;
    169 }
    170 
    171 std::unique_ptr<struct fiemap> alloc_fiemap(uint32_t extent_count)
    172 {
    173     size_t allocsize = offsetof(struct fiemap, fm_extents[extent_count]);
    174     std::unique_ptr<struct fiemap> res(new (::operator new (allocsize)) struct fiemap);
    175     memset(res.get(), 0, allocsize);
    176     res->fm_start = 0;
    177     res->fm_length = UINT64_MAX;
    178     res->fm_flags = 0;
    179     res->fm_extent_count = extent_count;
    180     res->fm_mapped_extents = 0;
    181     return res;
    182 }
    183 
    184 // Given a file path, look for the corresponding block device in /proc/mount
    185 std::string block_device_for_path(const std::string &path)
    186 {
    187     std::unique_ptr<FILE, int(*)(FILE*)> mnts(setmntent("/proc/mounts", "re"), endmntent);
    188     if (!mnts) {
    189         PLOG(ERROR) << "Unable to open /proc/mounts";
    190         return "";
    191     }
    192     std::string result;
    193     size_t best_length = 0;
    194     struct mntent *mnt; // getmntent returns a thread local, so it's safe.
    195     while ((mnt = getmntent(mnts.get())) != nullptr) {
    196         auto l = strlen(mnt->mnt_dir);
    197         if (l > best_length &&
    198             path.size() > l &&
    199             path[l] == '/' &&
    200             path.compare(0, l, mnt->mnt_dir) == 0) {
    201                 result = mnt->mnt_fsname;
    202                 best_length = l;
    203         }
    204     }
    205     if (result.empty()) {
    206         LOG(ERROR) <<"Didn't find a mountpoint to match path " << path;
    207         return "";
    208     }
    209     LOG(DEBUG) << "For path " << path << " block device is " << result;
    210     return result;
    211 }
    212 
    213 bool overwrite_with_zeros(int fd, off64_t start, off64_t length) {
    214     if (lseek64(fd, start, SEEK_SET) != start) {
    215         PLOG(ERROR) << "Seek failed for zero overwrite";
    216         return false;
    217     }
    218     char buf[BUFSIZ];
    219     memset(buf, 0, sizeof(buf));
    220     while (length > 0) {
    221         size_t wlen = static_cast<size_t>(std::min(static_cast<off64_t>(sizeof(buf)), length));
    222         auto written = write(fd, buf, wlen);
    223         if (written < 1) {
    224             PLOG(ERROR) << "Write of zeroes failed";
    225             return false;
    226         }
    227         length -= written;
    228     }
    229     return true;
    230 }
    231 
    232 }
    233