1// Copyright 2017 The Fuchsia Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <unistd.h> 6 7#include <fbl/alloc_checker.h> 8#include <fbl/unique_ptr.h> 9#include <fvm/fvm-lz4.h> 10 11#include "fvm/container.h" 12 13#define DEFAULT_SLICE_SIZE (8lu * (1 << 20)) 14 15int usage(void) { 16 fprintf(stderr, "usage: fvm [ output_path ] [ command ] [ <flags>* ] [ <input_paths>* ]\n"); 17 fprintf(stderr, "fvm performs host-side FVM and sparse file creation\n"); 18 fprintf(stderr, "Commands:\n"); 19 fprintf(stderr, " create : Creates an FVM partition\n"); 20 fprintf(stderr, " add : Adds a Minfs or Blobfs partition to an FVM (input path is" 21 " required)\n"); 22 fprintf(stderr, " extend : Extends an FVM container to the specified size (length is" 23 " required)\n"); 24 fprintf(stderr, " sparse : Creates a sparse file. One or more input paths are required.\n"); 25 fprintf(stderr, " verify : Report basic information about sparse/fvm files and run fsck on" 26 " contained partitions\n"); 27 fprintf(stderr, " decompress : Decompresses a compressed sparse file. --sparse input path is" 28 " required.\n"); 29 fprintf(stderr, "Flags (neither or both of offset/length must be specified):\n"); 30 fprintf(stderr, " --slice [bytes] - specify slice size (default: %zu)\n", DEFAULT_SLICE_SIZE); 31 fprintf(stderr, " --offset [bytes] - offset at which container begins (fvm only)\n"); 32 fprintf(stderr, " --length [bytes] - length of container within file (fvm only)\n"); 33 fprintf(stderr, " --compress - specify that file should be compressed (sparse only)\n"); 34 fprintf(stderr, " --zxcrypt - specify that data be placed in zxcrypt volume (sparse only)\n"); 35 fprintf(stderr, "Input options:\n"); 36 fprintf(stderr, " --blob [path] - Add path as blob type (must be blobfs)\n"); 37 fprintf(stderr, " --data [path] - Add path as data type (must be minfs)\n"); 38 fprintf(stderr, " --system [path] - Add path as system type (must be minfs)\n"); 39 fprintf(stderr, " --default [path] - Add generic path\n"); 40 fprintf(stderr, " --sparse [path] - Path to compressed sparse file\n"); 41 exit(-1); 42} 43 44int add_partitions(Container* container, int argc, char** argv) { 45 for (unsigned i = 0; i < argc; i += 2) { 46 if (argc - i < 2 || argv[i][0] != '-' || argv[i][1] != '-') { 47 usage(); 48 } 49 50 char* partition_type = argv[i] + 2; 51 char* partition_path = argv[i + 1]; 52 if ((container->AddPartition(partition_path, partition_type)) != ZX_OK) { 53 fprintf(stderr, "Failed to add partition\n"); 54 return -1; 55 } 56 } 57 58 return 0; 59} 60 61size_t get_disk_size(const char* path, size_t offset) { 62 fbl::unique_fd fd(open(path, O_RDONLY, 0644)); 63 64 if (fd) { 65 struct stat s; 66 if (fstat(fd.get(), &s) < 0) { 67 fprintf(stderr, "Failed to stat %s\n", path); 68 exit(-1); 69 } 70 71 return s.st_size - offset; 72 } 73 74 return 0; 75} 76 77int parse_size(const char* size_str, size_t* out) { 78 char* end; 79 size_t size = strtoull(size_str, &end, 10); 80 81 switch (end[0]) { 82 case 'K': 83 case 'k': 84 size *= 1024; 85 end++; 86 break; 87 case 'M': 88 case 'm': 89 size *= (1024 * 1024); 90 end++; 91 break; 92 case 'G': 93 case 'g': 94 size *= (1024 * 1024 * 1024); 95 end++; 96 break; 97 } 98 99 if (end[0] || size == 0) { 100 fprintf(stderr, "Bad size: %s\n", size_str); 101 return -1; 102 } 103 104 *out = size; 105 return 0; 106} 107 108int main(int argc, char** argv) { 109 if (argc < 3) { 110 usage(); 111 } 112 113 unsigned i = 1; 114 const char* path = argv[i++]; // Output path 115 const char* command = argv[i++]; // Command 116 117 size_t length = 0; 118 size_t offset = 0; 119 size_t slice_size = DEFAULT_SLICE_SIZE; 120 bool should_unlink = true; 121 uint32_t flags = 0; 122 while (i < argc) { 123 if (!strcmp(argv[i], "--slice") && i + 1 < argc) { 124 if (parse_size(argv[++i], &slice_size) < 0) { 125 return -1; 126 } 127 if (!slice_size || 128 slice_size % blobfs::kBlobfsBlockSize || 129 slice_size % minfs::kMinfsBlockSize) { 130 fprintf(stderr, "Invalid slice size - must be a multiple of %u and %u\n", 131 blobfs::kBlobfsBlockSize, minfs::kMinfsBlockSize); 132 return -1; 133 } 134 } else if (!strcmp(argv[i], "--offset") && i + 1 < argc) { 135 should_unlink = false; 136 if (parse_size(argv[++i], &offset) < 0) { 137 return -1; 138 } 139 } else if (!strcmp(argv[i], "--length") && i + 1 < argc) { 140 if (parse_size(argv[++i], &length) < 0) { 141 return -1; 142 } 143 } else if (!strcmp(argv[i], "--compress")) { 144 if (!strcmp(argv[++i], "lz4")) { 145 flags |= fvm::kSparseFlagLz4; 146 } else { 147 fprintf(stderr, "Invalid compression type\n"); 148 return -1; 149 } 150 } else if (!strcmp(argv[i], "--zxcrypt")) { 151 flags |= fvm::kSparseFlagZxcrypt; 152 } else { 153 break; 154 } 155 156 ++i; 157 } 158 159 if (!strcmp(command, "create") && should_unlink) { 160 unlink(path); 161 } 162 163 // If length was not specified, use remainder of file after offset 164 if (length == 0) { 165 length = get_disk_size(path, offset); 166 } 167 168 if (!strcmp(command, "create")) { 169 // If length was specified, an offset was not, we were asked to create a 170 // file, and the file does not exist, truncate it to the given length. 171 if (length != 0 && offset == 0) { 172 fbl::unique_fd fd(open(path, O_CREAT|O_EXCL|O_WRONLY, 0644)); 173 174 if (fd) { 175 ftruncate(fd.get(), length); 176 } 177 } 178 179 fbl::unique_ptr<FvmContainer> fvmContainer; 180 if (FvmContainer::Create(path, slice_size, offset, length, &fvmContainer) != ZX_OK) { 181 return -1; 182 } 183 184 if (add_partitions(fvmContainer.get(), argc - i, argv + i) < 0) { 185 return -1; 186 } 187 188 if (fvmContainer->Commit() != ZX_OK) { 189 return -1; 190 } 191 } else if (!strcmp(command, "add")) { 192 fbl::AllocChecker ac; 193 fbl::unique_ptr<FvmContainer> fvmContainer(new (&ac) FvmContainer(path, slice_size, offset, 194 length)); 195 if (!ac.check()) { 196 return ZX_ERR_NO_MEMORY; 197 } 198 199 if (add_partitions(fvmContainer.get(), argc - i, argv + i) < 0) { 200 return -1; 201 } 202 203 if (fvmContainer->Commit() != ZX_OK) { 204 return -1; 205 } 206 } else if (!strcmp(command, "extend")) { 207 if (length == 0 || offset > 0) { 208 usage(); 209 } 210 211 size_t disk_size = get_disk_size(path, 0); 212 213 if (length <= disk_size) { 214 fprintf(stderr, "Cannot extend to a value %zu less than current size %zu\n", length, 215 disk_size); 216 usage(); 217 } 218 219 fbl::AllocChecker ac; 220 fbl::unique_ptr<FvmContainer> fvmContainer(new (&ac) FvmContainer(path, slice_size, offset, 221 disk_size)); 222 if (!ac.check()) { 223 return ZX_ERR_NO_MEMORY; 224 } 225 226 if (fvmContainer->Extend(length) != ZX_OK) { 227 return -1; 228 } 229 } else if (!strcmp(command, "sparse")) { 230 if (offset) { 231 fprintf(stderr, "Invalid sparse flags\n"); 232 return -1; 233 } 234 235 fbl::unique_ptr<SparseContainer> sparseContainer; 236 if (SparseContainer::Create(path, slice_size, flags, &sparseContainer) != ZX_OK) { 237 return -1; 238 } 239 240 if (add_partitions(sparseContainer.get(), argc - i, argv + i) < 0) { 241 return -1; 242 } 243 244 if (sparseContainer->Commit() != ZX_OK) { 245 return -1; 246 } 247 } else if (!strcmp(command, "verify")) { 248 fbl::unique_ptr<Container> containerData; 249 if (Container::Create(path, offset, length, flags, &containerData) != ZX_OK) { 250 return -1; 251 } 252 253 if (containerData->Verify() != ZX_OK) { 254 return -1; 255 } 256 } else if (!strcmp(command, "decompress")) { 257 if (argc - i != 2) { 258 usage(); 259 return -1; 260 } 261 262 char* input_type = argv[i]; 263 char* input_path = argv[i + 1]; 264 265 if (strcmp(input_type, "--sparse")) { 266 usage(); 267 return -1; 268 } 269 270 if (fvm::decompress_sparse(input_path, path) != ZX_OK) { 271 return -1; 272 } 273 274 fbl::unique_ptr<SparseContainer> sparseData(new SparseContainer(path, slice_size, flags)); 275 if (sparseData->Verify() != ZX_OK) { 276 return -1; 277 } 278 } else { 279 usage(); 280 } 281 282 return 0; 283} 284