blob: 68455638c020198f2d6fcd93477a0ed4325d2836 [file] [log] [blame]
Jeff Sharkey37ba1252018-01-19 10:55:18 +09001/*
2 * Copyright (C) 2018 The Android Open Source Project
Tuxera Inc0bf909b2021-07-27 21:17:09 +03003 * Copyright (C) 2010-2021 Tuxera Inc.
Jeff Sharkey37ba1252018-01-19 10:55:18 +09004 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
Tuxera Inc0bf909b2021-07-27 21:17:09 +030018#include <fcntl.h>
Jeff Sharkey37ba1252018-01-19 10:55:18 +090019#include <sys/mount.h>
20
Tuxera Inc0bf909b2021-07-27 21:17:09 +030021#define LOG_TAG "Vold::Exfat"
22
Jeff Sharkey37ba1252018-01-19 10:55:18 +090023#include <android-base/logging.h>
24#include <android-base/stringprintf.h>
25
26#include <logwrap/logwrap.h>
27
28#include "Exfat.h"
29#include "Utils.h"
30
31using android::base::StringPrintf;
32
33namespace android {
34namespace vold {
35namespace exfat {
36
Tuxera Inc0bf909b2021-07-27 21:17:09 +030037static const char* kMkfsPath = "/system/bin/mkexfat";
38static const char* kFsckPath = "/system/bin/exfatck";
39
40status_t Detect(const std::string& source, bool *outResult) {
41 const char *const fsPath = source.c_str();
42
43 int retval = -1;
44
45 int fd = open(fsPath, O_RDONLY | O_CLOEXEC);
46 if (fd != -1) {
47 loff_t seek_res = lseek64(fd, 0, SEEK_SET);
48 if (seek_res == 0) {
49 char sb_region[4096];
50 ssize_t read_res = read(fd, sb_region, 4096);
51 if (read_res >= 512) {
52 if (!memcmp(&sb_region[3], "EXFAT ", 8)) {
53 LOG(INFO) << "exFAT filesystem detected.";
54 *outResult = true;
55 }
56 else if (!memcmp(&sb_region[0], "RRaAXFAT ", 11)) {
57 LOG(INFO) << "Corrupted exFAT filesystem detected. Fixing.";
58 *outResult = true;
59 }
60 else {
61 LOG(VERBOSE) << "Filesystem detection failed (not an exFAT "
62 << "filesystem).";
63 *outResult = false;
64 }
65
66 retval = 0;
67 }
68 else if (read_res != -1) {
69 int err = errno ? errno : EIO;
70 LOG(ERROR) << "Error while reading 4096 bytes from device "
71 << "offset 0: " << strerror(errno) << " (" << errno
72 << ")";
73 errno = err;
74 }
75 }
76 else if (seek_res != -1) {
77 int err = errno ? errno : EIO;
78 LOG(ERROR) << "Error while seeking to device offset 0: "
79 << strerror(errno) << " (" << errno << ")";
80 errno = err;
81 }
82
83 close(fd);
84 }
85
86 return retval;
87}
Jeff Sharkey37ba1252018-01-19 10:55:18 +090088
89bool IsSupported() {
90 return access(kMkfsPath, X_OK) == 0 && access(kFsckPath, X_OK) == 0 &&
Tuxera Inc0bf909b2021-07-27 21:17:09 +030091 IsFilesystemSupported("texfat");
Jeff Sharkey37ba1252018-01-19 10:55:18 +090092}
93
94status_t Check(const std::string& source) {
95 std::vector<std::string> cmd;
96 cmd.push_back(kFsckPath);
Tuxera Inc0bf909b2021-07-27 21:17:09 +030097 cmd.push_back("-r");
Jeff Sharkey37ba1252018-01-19 10:55:18 +090098 cmd.push_back(source);
99
Paul Crowleyde2d6202018-11-30 11:43:47 -0800100 int rc = ForkExecvp(cmd, nullptr, sFsckUntrustedContext);
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900101 if (rc == 0) {
102 LOG(INFO) << "Check OK";
103 return 0;
104 } else {
105 LOG(ERROR) << "Check failed (code " << rc << ")";
Tuxera Inc0bf909b2021-07-27 21:17:09 +0300106
107 /* Ignore return/errno value. We know it's exFAT, so any file system
108 * check/repair results should be considered a bonus. The driver should
109 * be able to mount the volume in any case and deal with inconsistencies
110 * appropriately. */
111 errno = 0;
112 return 0;
113 }
114}
115
116status_t Mount(const std::string& source, const std::string& target, bool ro,
117 bool remount, bool executable, int ownerUid, int ownerGid, int permMask)
118{
119 int rc;
120 unsigned long flags;
121 auto mountData = android::base::StringPrintf("utf8,uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid,
122 ownerGid, permMask, permMask);
123
124 flags = MS_NODEV | MS_NOSUID | MS_DIRSYNC | MS_NOATIME;
125
126 flags |= (executable ? 0 : MS_NOEXEC);
127 flags |= (ro ? MS_RDONLY : 0);
128 flags |= (remount ? MS_REMOUNT : 0);
129
130 rc = mount(source.c_str(), target.c_str(), "texfat", flags, mountData.c_str());
131
132 if (rc && errno == EROFS) {
133 LOG(ERROR) << source << "appears to be a read only filesystem - retrying mount RO";
134 flags |= MS_RDONLY;
135 rc = mount(source.c_str(), target.c_str(), "texfat", flags, mountData.c_str());
136 }
137
138 return rc;
139}
140
141static status_t DoFormat(const std::string& source, unsigned long numSectors,
142 bool wholeDevice, bool partitionOnly, bool wipe) {
143 const char *const fsPath = source.c_str();
144
145 if (!wholeDevice && partitionOnly) {
146 LOG(ERROR) << "Cannot partition non-whole device.";
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900147 errno = EIO;
148 return -1;
149 }
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900150
Tuxera Inc0bf909b2021-07-27 21:17:09 +0300151 LOG(INFO) << (partitionOnly ? "Partitioning" : "Formatting")
152 << " SDXC " << (partitionOnly ? "card" : "exFAT file system")
153 << " at \"" << fsPath << "\" as "
154 << (wholeDevice ? "whole device" : "single partition") << "...";
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900155
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900156 std::vector<std::string> cmd;
157 cmd.push_back(kMkfsPath);
Tuxera Inc0bf909b2021-07-27 21:17:09 +0300158 if (wholeDevice)
159 cmd.push_back("--sda-whole");
160 cmd.push_back("--sda-strict");
161
162 if (partitionOnly) {
163 cmd.push_back("--sda-partitioning-only");
164 }
165
166 if (wipe) {
167 cmd.push_back("--discard");
168 }
169
170 if (numSectors) {
171 char tmp[32];
172 snprintf(tmp, sizeof(tmp), "%lu", numSectors);
173 cmd.push_back("--sector-count");
174 cmd.push_back(tmp);
175 }
176
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900177 cmd.push_back(source);
178
179 int rc = ForkExecvp(cmd);
180 if (rc == 0) {
181 LOG(INFO) << "Format OK";
182 return 0;
183 } else {
184 LOG(ERROR) << "Format failed (code " << rc << ")";
185 errno = EIO;
186 return -1;
187 }
188 return 0;
189}
190
Tuxera Inc0bf909b2021-07-27 21:17:09 +0300191status_t Format(const std::string& source, unsigned long numSectors) {
192 return DoFormat(source, numSectors, false, false, false);
193}
194
195static int getDeviceSize(const char *const devicePath, uint64_t *const outSize)
196{
197 int err = 0;
198 int fd = open(devicePath, O_RDONLY | O_CLOEXEC);
199 if (fd == -1)
200 err = errno ? errno : EIO;
201 else {
202 uint64_t size;
203 if (ioctl(fd, BLKGETSIZE64, &size) == -1)
204 err = errno ? errno : EIO;
205 else
206 *outSize = size;
207
208 close(fd);
209 }
210
211 return err;
212}
213
214int CheckSize(const std::string& source, bool isPartition) {
215 const char *const wholeDevicePath = source.c_str();
216
217 const unsigned long long limit_min =
218 isPartition ? 34342961152ULL : 34359738368ULL;
219 const unsigned long long limit_max =
220 isPartition ? 2198956146688ULL : 2199023255552ULL;
221
222 int res;
223 uint64_t size = 0;
224 int err = getDeviceSize(wholeDevicePath, &size);
225 if (!err) {
226 if (size < limit_min || size > limit_max)
227 res = 0;
228 else
229 res = 1;
230 }
231 else {
232 res = -1;
233 errno = err;
234 }
235
236 return res;
237}
238
239status_t PartitionDisk(const std::string& source) {
240 return DoFormat(source, 0, true, true, false);
241}
242
243#ifndef VFAT_IOCTL_GET_VOLUME_ID
244#define VFAT_IOCTL_GET_VOLUME_ID _IOR('r', 0x12, __u32)
245#endif
246
247extern "C" {
248 typedef struct _TUXERA_IOCTL_GET_VOLUME_LABEL_ARGS {
249 __u32 size; /* Size in bytes of volume label buffer. */
250 __u8 label[]; /* Buffer to return the volume label into of size at least @size
251 bytes. */
252 } __attribute__ ((packed)) TUXERA_VOLUME_LABEL;
253}
254
255#ifndef TUXERA_GET_VOLUME_LABEL
256#define TUXERA_GET_VOLUME_LABEL _IOWR('X', 2, TUXERA_VOLUME_LABEL)
257#endif
258
259int ExtractMetadata(const char *mountPoint, char **outVolumeLabel,
260 char **outVolumeUuid)
261{
262 int err = 0;
263 __u32 returnedLabelSize;
264 __u32 volumeLabelSize;
265 TUXERA_VOLUME_LABEL *volumeLabel = NULL;
266 __u32 volumeLabelBufferSize = 0;
267 char *volumeLabelBuffer = NULL;
268 __u32 volumeUuidBufferSize = 0;
269 char *volumeUuidBuffer = NULL;
270 int fd = -1;
271 int volumeId;
272
273 fd = open(mountPoint, O_RDONLY | O_CLOEXEC);
274 if(fd == -1) {
275 err = errno ? errno : ENOENT;
276 LOG(ERROR) << "Error while opening mount point: " << strerror(errno)
277 << " (" << errno << ")";
278 goto out;
279 }
280
281 volumeLabelSize = 4096;
282 while(1) {
283 TUXERA_VOLUME_LABEL *newVolumeLabel;
284 int res;
285
286 newVolumeLabel = (TUXERA_VOLUME_LABEL*) realloc(volumeLabel,
287 sizeof(TUXERA_VOLUME_LABEL) + volumeLabelSize);
288 if(!newVolumeLabel) {
289 err = errno ? errno : ENOMEM;
290 LOG(ERROR) << "Error while reallocating memory (" << volumeLabelSize
291 << " bytes) for TUXERA_VOLUME_LABEL: " << strerror(errno)
292 << " (" << errno << ")";
293 goto out;
294 }
295
296 volumeLabel = newVolumeLabel;
297
298 memset(volumeLabel, 0, sizeof(TUXERA_VOLUME_LABEL) + volumeLabelSize);
299 volumeLabel->size = volumeLabelSize;
300
301 if((res = ioctl(fd, TUXERA_GET_VOLUME_LABEL, volumeLabel)) < 0) {
302 err = errno ? errno : EINVAL;
303 LOG(ERROR) << "Error while issuing IOCTL to get volume label: "
304 << strerror(errno) << " (" << errno << ")";
305 goto out;
306 }
307
308 returnedLabelSize = (__u32) res;
309
310 if(volumeLabelSize >= returnedLabelSize + 1) {
311 break;
312 }
313
314 volumeLabelSize = returnedLabelSize + 1;
315 }
316
317 volumeLabelBufferSize = volumeLabel->size + 1;
318 volumeLabelBuffer = (char*) malloc(volumeLabelBufferSize);
319 if(!volumeLabelBuffer) {
320 err = errno ? errno : ENOMEM;
321 LOG(ERROR) << "Error while allocating volume label buffer: "
322 << strerror(errno) << " (" << errno << ")";
323 goto out;
324 }
325
326 memcpy(volumeLabelBuffer, volumeLabel->label, volumeLabel->size);
327 volumeLabelBuffer[volumeLabel->size] = '\0';
328
329 volumeUuidBufferSize = 10;
330 volumeUuidBuffer = (char*) malloc(volumeUuidBufferSize);
331 if(!volumeUuidBuffer) {
332 err = errno ? errno : ENOMEM;
333 LOG(ERROR) << "Error while allocating volume UUID buffer: "
334 << strerror(errno) << " (" << errno << ")";
335 goto out;
336 }
337
338 volumeId = ioctl(fd, VFAT_IOCTL_GET_VOLUME_ID, NULL);
339 if(volumeId == -1) {
340 err = errno ? errno : EINVAL;
341 LOG(ERROR) << "Error while issuing IOCTL to get volume ID: "
342 << strerror(errno) << " (" << errno << ")";
343 goto out;
344 }
345
346 if(snprintf(volumeUuidBuffer, volumeUuidBufferSize, "%04X-%04X",
347 (volumeId >> 16) & 0xFFFF, volumeId & 0xFFFF) != 9)
348 {
349 err = errno ? errno : EINVAL;
350 LOG(ERROR) << "Error while constructing volume UUID string: "
351 << strerror(errno) << " (" << errno << ")";
352 goto out;
353 }
354
355out:
356 if(err) {
357 if(volumeUuidBuffer) {
358 free(volumeUuidBuffer);
359 }
360
361 if(volumeLabelBuffer) {
362 free(volumeLabelBuffer);
363 }
364 }
365 else {
366 *outVolumeLabel = volumeLabelBuffer;
367 *outVolumeUuid = volumeUuidBuffer;
368 }
369
370 if(volumeLabel) {
371 free(volumeLabel);
372 }
373
374 if(fd != -1) {
375 close(fd);
376 }
377
378 return err;
379}
380
Jeff Sharkey37ba1252018-01-19 10:55:18 +0900381} // namespace exfat
382} // namespace vold
383} // namespace android