master
cpp 202 lines 5.7 KB
Raw
1 // Copyright (C) Microsoft Corporation. All rights reserved.
2 #include <string>
3 #include <string_view>
4 #include <cerrno>
5 #include <sys/types.h>
6 #include <sys/mount.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9 #include <lxdef.h>
10 #include <lxwil.h>
11 #include "mountutilcpp.h"
12
13 namespace {
14
15 enum class ParseFlags
16 {
17 None = 0,
18 Remove = 0x1,
19 NoFail = 0x2,
20 OptionalValue = 0x4,
21 };
22
23 #pragma clang diagnostic push
24 #pragma clang diagnostic ignored "-Wunused-function"
25 DEFINE_ENUM_FLAG_OPERATORS(ParseFlags);
26 #pragma clang diagnostic pop
27
28 struct MountFlag
29 {
30 const char* Name;
31 int MountFlags;
32 ParseFlags ParseFlags;
33 };
34
35 #define FLAG_WITH_NAMED_INVERSE(name, inverse, flag) \
36 {(name), (flag), ParseFlags::None}, \
37 { \
38 (inverse), (flag), ParseFlags::Remove \
39 }
40
41 // "opt", "noopt" pair where the "noopt" version adds a flag, and "opt" removes it.
42 #define NO_FLAG_WITH_INVERSE(name, flag) FLAG_WITH_NAMED_INVERSE("no" name, name, flag)
43
44 // "opt", "noopt" pair where the "opt" version adds a flag, and "noopt" removes it.
45 #define FLAG_WITH_INVERSE(name, flag) FLAG_WITH_NAMED_INVERSE(name, "no" name, flag)
46
47 // List of mount options that translate into mount flags.
48 // This is based on the information in the mount(8) manpage. Note that not all options are present,
49 // since this is intended to be used by a mount helper, and not all options are forwarded to the
50 // helpers by /bin/mount.
51 const MountFlag c_flagMap[] = {
52 FLAG_WITH_NAMED_INVERSE("sync", "async", MS_SYNCHRONOUS),
53 NO_FLAG_WITH_INVERSE("atime", MS_NOATIME),
54 {"defaults", 0, ParseFlags::None},
55 NO_FLAG_WITH_INVERSE("dev", MS_NODEV),
56 NO_FLAG_WITH_INVERSE("diratime", MS_NODIRATIME),
57 {"dirsync", MS_DIRSYNC, ParseFlags::None},
58 NO_FLAG_WITH_INVERSE("exec", MS_NOEXEC),
59 {"group", MS_NOSUID | MS_NODEV, ParseFlags::None},
60 {"nogroup", 0, ParseFlags::None},
61 FLAG_WITH_INVERSE("iversion", MS_I_VERSION),
62 FLAG_WITH_INVERSE("mand", MS_MANDLOCK),
63 {"_netdev", 0, ParseFlags::None},
64 {"nofail", 0, ParseFlags::NoFail},
65 FLAG_WITH_INVERSE("relatime", MS_RELATIME),
66 FLAG_WITH_INVERSE("strictatime", MS_STRICTATIME),
67 FLAG_WITH_INVERSE("lazytime", MS_LAZYTIME),
68 NO_FLAG_WITH_INVERSE("suid", MS_NOSUID),
69 FLAG_WITH_NAMED_INVERSE("silent", "loud", MS_SILENT),
70 {"owner", MS_NODEV | MS_NOSUID, ParseFlags::None},
71 {"noowner", 0, ParseFlags::None},
72 {"remount", MS_REMOUNT, ParseFlags::None},
73 FLAG_WITH_NAMED_INVERSE("ro", "rw", MS_RDONLY),
74 {"user", MS_NOEXEC | MS_NODEV | MS_NOSUID, ParseFlags::OptionalValue},
75 {"nouser", 0, ParseFlags::None},
76 {"users", MS_NOEXEC | MS_NODEV | MS_NOSUID, ParseFlags::None},
77 {"nousers", 0, ParseFlags::None},
78 };
79
80 // Determine if an option should be a flag.
81 // Returns the flag information if found; otherwise, null.
82 const MountFlag* FindOption(std::string_view option)
83 {
84 // Check if the option has a value.
85 bool hasValue = false;
86 auto index = option.find_first_of('=');
87 if (index != std::string_view::npos)
88 {
89 hasValue = true;
90 option = option.substr(0, index);
91 }
92
93 for (auto& flag : c_flagMap)
94 {
95 // If the option has a value, ignore the entry if it doesn't allow one.
96 if (hasValue && !WI_IsFlagSet(flag.ParseFlags, ParseFlags::OptionalValue))
97 {
98 continue;
99 }
100
101 if (option == flag.Name)
102 {
103 return &flag;
104 }
105 }
106
107 return nullptr;
108 }
109
110 // Retrieves the next character-separated token from a string view, returning
111 // the token and updating the view to be the remainder of the string.
112 std::string_view NextToken(std::string_view& view, char separator)
113 {
114 std::string_view result;
115 auto pos = view.find_first_of(separator);
116 if (pos == view.npos)
117 {
118 result = view;
119 view = {};
120 }
121 else
122 {
123 result = view.substr(0, pos);
124 view = view.substr(pos + 1);
125 }
126
127 return result;
128 }
129
130 } // namespace
131
132 namespace mountutil {
133
134 ParsedOptions MountParseFlags(std::string_view options)
135 {
136 ParsedOptions result{};
137 while (!options.empty())
138 {
139 // Get the next option and check if it's a flag.
140 auto option = NextToken(options, ',');
141 if (option.empty())
142 {
143 continue;
144 }
145
146 auto flag = FindOption(option);
147 if (flag == nullptr)
148 {
149 // Not a flag, so append to the string options.
150 if (!result.StringOptions.empty())
151 {
152 result.StringOptions += ',';
153 }
154
155 result.StringOptions += option;
156 }
157 else
158 {
159 // Modify the mount flags.
160 if (WI_IsFlagSet(flag->ParseFlags, ParseFlags::Remove))
161 {
162 WI_ClearAllFlags(result.MountFlags, flag->MountFlags);
163 }
164 else
165 {
166 WI_SetAllFlags(result.MountFlags, flag->MountFlags);
167 }
168
169 if (WI_IsFlagSet(flag->ParseFlags, ParseFlags::NoFail))
170 {
171 result.NoFail = true;
172 }
173 }
174 }
175
176 return result;
177 }
178
179 int MountFilesystem(const char* source, const char* target, const char* type, const char* options)
180 {
181 auto parsedOptions = MountParseFlags(options);
182 int result = mount(source, target, type, parsedOptions.MountFlags, parsedOptions.StringOptions.c_str());
183
184 // If the nofail option was specified, ENOENT on the source only must be ignored.
185 if (result < 0 && errno == ENOENT && parsedOptions.NoFail)
186 {
187 struct stat st;
188
189 // If the target exists, the error must be about the source, so ignore it.
190 if (stat(target, &st) == 0)
191 {
192 return 0;
193 }
194
195 // In case stat changed errno.
196 errno = ENOENT;
197 }
198
199 return result;
200 }
201
202 } // namespace mountutil