1 // Copyright 2018 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 <fcntl.h>
6 #include <mutex>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <unistd.h>
11 
12 #include <fbl/string.h>
13 #include <fbl/unique_fd.h>
14 #include <fbl/vector.h>
15 #include <zircon/types.h>
16 
17 // The "manifest" command is only being retained here for backwards compatibility.
18 //TODO(planders): Once all clients have switched create/add with --manifest, remove this command.
19 enum class Command {
20     kNone,
21     kMkfs,
22     kFsck,
23     kLs,
24     kAdd,
25     kCp,
26     kManifest,
27     kMkdir,
28 };
29 
30 enum class Option {
31     kDepfile,
32     kReadonly,
33     kOffset,
34     kLength,
35     kCompress,
36     kHelp,
37 };
38 
39 enum class Argument {
40     kManifest,
41     kBlob,
42 };
43 
44 enum class ArgType {
45     kNone,
46     kOne,
47     kTwo,
48     kMany,
49     kOptional,
50 };
51 
52 // An abstract class which defines an interface for processing and running commands for file system
53 // host-side tools. This includes parsing all command line options, pre-processing any files to be
54 // copied, and resizing the file system image as necessary. Child classes must implement any
55 // commands they wish to support, as well as providing their own space calculations for files to be
56 // added.
57 class FsCreator {
58 public:
59     DISALLOW_COPY_ASSIGN_AND_MOVE(FsCreator);
60 
FsCreator(uint64_t data_blocks)61     FsCreator(uint64_t data_blocks) : data_blocks_(data_blocks),command_(Command::kNone),
62                                       offset_(0), length_(0), read_only_(false), compress_(false),
63                                       depfile_lock_() {}
~FsCreator()64     virtual ~FsCreator() {}
65 
66     // Process the command line arguments and run the specified command.
67     zx_status_t ProcessAndRun(int argc, char** argv);
68 
69     // If a depfile was requested, |str| will be appended (followed by a space)
70     // to the depfile. |str| must be less than PATH_MAX.
71     zx_status_t AppendDepfile(const char* str);
72 
73 protected:
74     // Print usage information for all options, commands, and arguments valid for this fs.
75     virtual zx_status_t Usage();
76 
77     // Returns the command name of the child fs.
78     virtual const char* GetToolName() = 0;
79 
80     // Tells whether a given |command|, |option|, or |argument| are valid for this fs.
81     virtual bool IsCommandValid(Command command) = 0;
82     virtual bool IsOptionValid(Option option) = 0;
83     virtual bool IsArgumentValid(Argument argument) = 0;
84 
85     // Processes the manifest at |manifest_path| and adds all relevant source/destination files to
86     // the child's internal processing lists.
87     zx_status_t ProcessManifest(char* manifest_path);
88 
89     // Parses the next line in the |manifest| file located at |dir_path|,
90     // and returns the |dst| and |src| paths (if found).
91     zx_status_t ParseManifestLine(FILE* manifest, const char* dir_path, char* src, char* dst);
92 
93     // Processes one line in |manifest|, storing files to copy and calculating total space required.
94     // Returns "ZX_ERR_OUT_OF_RANGE" when manifest has reached EOF.
95     virtual zx_status_t ProcessManifestLine(FILE* manifest, const char* dir_path) = 0;
96 
97     // Process custom arguments specific to the child fs. Returns the number of processed arguments
98     // in |processed|.
ProcessCustom(int argc,char ** argv,uint8_t * processed)99     virtual zx_status_t ProcessCustom(int argc, char** argv, uint8_t* processed) {
100         return ZX_ERR_NOT_SUPPORTED;
101     }
102 
103     // Calculates the minimum fs size required for all files processed up to this point.
104     virtual zx_status_t CalculateRequiredSize(off_t* out) = 0;
105 
106     // Commands.
107     // Creates the fs at fd_.
Mkfs()108     virtual zx_status_t Mkfs() { return ZX_ERR_NOT_SUPPORTED; }
109     // Runs fsck on the fs at fd_.
Fsck()110     virtual zx_status_t Fsck() { return ZX_ERR_NOT_SUPPORTED; }
111     // Adds all files specified in manifests or other command line arguments to the fs.
Add()112     virtual zx_status_t Add()  { return ZX_ERR_NOT_SUPPORTED; }
113     // Runs ls on the fs at fd_, at the specified path (if any).
Ls()114     virtual zx_status_t Ls()   { return ZX_ERR_NOT_SUPPORTED; }
115 
GetCommand()116     Command GetCommand() const { return command_; }
GetOffset()117     off_t GetOffset() const { return offset_; }
GetLength()118     off_t GetLength() const { return length_; }
ShouldCompress()119     bool ShouldCompress() const { return compress_; }
120 
121     fbl::unique_fd fd_;
122 
123     off_t data_blocks_;
124 
125 private:
126     // Process all options/arguments and open fd to device.
127     zx_status_t ProcessArgs(int argc, char** argv);
128 
129     // Perform the specified command.
130     zx_status_t RunCommand();
131 
132     // Parses the size specification (if any) from the |device| string, and returns the result in
133     // |*out|. The size argument is only valid for the "create" command.
134     zx_status_t ParseSize(char* device, size_t* out);
135 
136     // Resizes the file on "create" if a different size was specified, or the file is not as
137     // large as it needs to be to contain all specified files (specifiles).
138     zx_status_t ResizeFile(off_t requested_size, struct stat stats);
139 
140     Command command_;
141     off_t offset_;
142     off_t length_;
143     bool read_only_;
144     bool compress_;
145     std::mutex depfile_lock_;
146     fbl::unique_fd depfile_;
147 };
148