]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c
remove '=' from reserved space check (#1753)
[Ventoy.git] / LinuxGUI / Ventoy2Disk / Lib / exfat / src / libexfat / io.c
1 /*
2 io.c (02.09.09)
3 exFAT file system implementation library.
4
5 Free exFAT implementation.
6 Copyright (C) 2010-2018 Andrew Nayenko
7
8 This program is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with this program; if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "exfat.h"
24 #include <inttypes.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <errno.h>
31 #if defined(__APPLE__)
32 #include <sys/disk.h>
33 #elif defined(__OpenBSD__)
34 #include <sys/param.h>
35 #include <sys/disklabel.h>
36 #include <sys/dkio.h>
37 #include <sys/ioctl.h>
38 #elif __linux__
39 #include <sys/mount.h>
40 #endif
41 #ifdef USE_UBLIO
42 #include <sys/uio.h>
43 #include <ublio.h>
44 #endif
45
46 struct exfat_dev
47 {
48 int fd;
49 enum exfat_mode mode;
50 off_t size; /* in bytes */
51 #ifdef USE_UBLIO
52 off_t pos;
53 ublio_filehandle_t ufh;
54 #endif
55 };
56
57 int g_vtoy_exfat_disk_fd = -1;
58 uint64_t g_vtoy_exfat_part_size = 0;
59
60 static bool is_open(int fd)
61 {
62 return fcntl(fd, F_GETFD) != -1;
63 }
64
65 static int open_ro(const char* spec)
66 {
67 return open(spec, O_RDONLY);
68 }
69
70 static int open_rw(const char* spec)
71 {
72 int fd = open(spec, O_RDWR);
73 #ifdef __linux__
74 int ro = 0;
75
76 /*
77 This ioctl is needed because after "blockdev --setro" kernel still
78 allows to open the device in read-write mode but fails writes.
79 */
80 if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
81 {
82 close(fd);
83 errno = EROFS;
84 return -1;
85 }
86 #endif
87 return fd;
88 }
89
90 struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
91 {
92 struct exfat_dev* dev;
93 struct stat stbuf;
94 #ifdef USE_UBLIO
95 struct ublio_param up;
96 #endif
97
98 /* The system allocates file descriptors sequentially. If we have been
99 started with stdin (0), stdout (1) or stderr (2) closed, the system
100 will give us descriptor 0, 1 or 2 later when we open block device,
101 FUSE communication pipe, etc. As a result, functions using stdin,
102 stdout or stderr will actually work with a different thing and can
103 corrupt it. Protect descriptors 0, 1 and 2 from such misuse. */
104 while (!is_open(STDIN_FILENO)
105 || !is_open(STDOUT_FILENO)
106 || !is_open(STDERR_FILENO))
107 {
108 /* we don't need those descriptors, let them leak */
109 if (open("/dev/null", O_RDWR) == -1)
110 {
111 exfat_error("failed to open /dev/null");
112 return NULL;
113 }
114 }
115
116 dev = malloc(sizeof(struct exfat_dev));
117 if (dev == NULL)
118 {
119 exfat_error("failed to allocate memory for device structure");
120 return NULL;
121 }
122
123 switch (mode)
124 {
125 case EXFAT_MODE_RO:
126 dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
127 if (dev->fd == -1)
128 {
129 free(dev);
130 exfat_error("failed to open '%s' in read-only mode: %s", spec,
131 strerror(errno));
132 return NULL;
133 }
134 dev->mode = EXFAT_MODE_RO;
135 break;
136 case EXFAT_MODE_RW:
137 dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
138 if (dev->fd == -1)
139 {
140 free(dev);
141 exfat_error("failed to open '%s' in read-write mode: %s", spec,
142 strerror(errno));
143 return NULL;
144 }
145 dev->mode = EXFAT_MODE_RW;
146 break;
147 case EXFAT_MODE_ANY:
148 dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_rw(spec) : g_vtoy_exfat_disk_fd;
149 if (dev->fd != -1)
150 {
151 dev->mode = EXFAT_MODE_RW;
152 break;
153 }
154 dev->fd = g_vtoy_exfat_disk_fd < 0 ? open_ro(spec) : g_vtoy_exfat_disk_fd;
155 if (dev->fd != -1)
156 {
157 dev->mode = EXFAT_MODE_RO;
158 exfat_warn("'%s' is write-protected, mounting read-only", spec);
159 break;
160 }
161 free(dev);
162 exfat_error("failed to open '%s': %s", spec, strerror(errno));
163 return NULL;
164 }
165
166 if (fstat(dev->fd, &stbuf) != 0)
167 {
168 close(dev->fd);
169 free(dev);
170 exfat_error("failed to fstat '%s'", spec);
171 return NULL;
172 }
173 if (!S_ISBLK(stbuf.st_mode) &&
174 !S_ISCHR(stbuf.st_mode) &&
175 !S_ISREG(stbuf.st_mode))
176 {
177 close(dev->fd);
178 free(dev);
179 exfat_error("'%s' is neither a device, nor a regular file", spec);
180 return NULL;
181 }
182
183 #if defined(__APPLE__)
184 if (!S_ISREG(stbuf.st_mode))
185 {
186 uint32_t block_size = 0;
187 uint64_t blocks = 0;
188
189 if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
190 {
191 close(dev->fd);
192 free(dev);
193 exfat_error("failed to get block size");
194 return NULL;
195 }
196 if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
197 {
198 close(dev->fd);
199 free(dev);
200 exfat_error("failed to get blocks count");
201 return NULL;
202 }
203 dev->size = blocks * block_size;
204 }
205 else
206 #elif defined(__OpenBSD__)
207 if (!S_ISREG(stbuf.st_mode))
208 {
209 struct disklabel lab;
210 struct partition* pp;
211 char* partition;
212
213 if (ioctl(dev->fd, DIOCGDINFO, &lab) == -1)
214 {
215 close(dev->fd);
216 free(dev);
217 exfat_error("failed to get disklabel");
218 return NULL;
219 }
220
221 /* Don't need to check that partition letter is valid as we won't get
222 this far otherwise. */
223 partition = strchr(spec, '\0') - 1;
224 pp = &(lab.d_partitions[*partition - 'a']);
225 dev->size = DL_GETPSIZE(pp) * lab.d_secsize;
226
227 if (pp->p_fstype != FS_NTFS)
228 exfat_warn("partition type is not 0x07 (NTFS/exFAT); "
229 "you can fix this with fdisk(8)");
230 }
231 else
232 #endif
233 {
234 /* works for Linux, FreeBSD, Solaris */
235 dev->size = exfat_seek(dev, 0, SEEK_END);
236 if (dev->size <= 0)
237 {
238 close(dev->fd);
239 free(dev);
240 exfat_error("failed to get size of '%s'", spec);
241 return NULL;
242 }
243 if (exfat_seek(dev, 0, SEEK_SET) == -1)
244 {
245 close(dev->fd);
246 free(dev);
247 exfat_error("failed to seek to the beginning of '%s'", spec);
248 return NULL;
249 }
250 }
251
252 #ifdef USE_UBLIO
253 memset(&up, 0, sizeof(struct ublio_param));
254 up.up_blocksize = 256 * 1024;
255 up.up_items = 64;
256 up.up_grace = 32;
257 up.up_priv = &dev->fd;
258
259 dev->pos = 0;
260 dev->ufh = ublio_open(&up);
261 if (dev->ufh == NULL)
262 {
263 close(dev->fd);
264 free(dev);
265 exfat_error("failed to initialize ublio");
266 return NULL;
267 }
268 #endif
269
270 return dev;
271 }
272
273 int exfat_close(struct exfat_dev* dev)
274 {
275 int rc = 0;
276
277 #ifdef USE_UBLIO
278 if (ublio_close(dev->ufh) != 0)
279 {
280 exfat_error("failed to close ublio");
281 rc = -EIO;
282 }
283 #endif
284 if (dev->fd != g_vtoy_exfat_disk_fd)
285 {
286 if (close(dev->fd) != 0)
287 {
288 exfat_error("failed to close device: %s", strerror(errno));
289 rc = -EIO;
290 }
291 }
292
293 free(dev);
294 return rc;
295 }
296
297 int exfat_fsync(struct exfat_dev* dev)
298 {
299 int rc = 0;
300
301 #ifdef USE_UBLIO
302 if (ublio_fsync(dev->ufh) != 0)
303 {
304 exfat_error("ublio fsync failed");
305 rc = -EIO;
306 }
307 #endif
308 if (fsync(dev->fd) != 0)
309 {
310 exfat_error("fsync failed: %s", strerror(errno));
311 rc = -EIO;
312 }
313 return rc;
314 }
315
316 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
317 {
318 return dev->mode;
319 }
320
321
322 off_t exfat_get_size(const struct exfat_dev* dev)
323 {
324 return dev->size;
325 }
326
327 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
328 {
329 #ifdef USE_UBLIO
330 /* XXX SEEK_CUR will be handled incorrectly */
331 return dev->pos = lseek(dev->fd, offset, whence);
332 #else
333
334 if (SEEK_SET == whence)
335 {
336 if (offset > g_vtoy_exfat_part_size)
337 {
338 return -1;
339 }
340
341 lseek(dev->fd, 512 * 2048 + offset, SEEK_SET);
342 return offset;
343 }
344 else if (SEEK_END == whence)
345 {
346 if (offset == 0)
347 {
348 offset = 512 * 2048 + g_vtoy_exfat_part_size;
349 lseek(dev->fd, offset, SEEK_SET);
350 return (off_t)g_vtoy_exfat_part_size;
351 }
352 else
353 {
354 exfat_error("Invalid SEEK_END offset %llu", (unsigned long long)offset);
355 return -1;
356 }
357 }
358 else
359 {
360 exfat_error("Invalid seek whence %d", whence);
361 return lseek(dev->fd, offset, whence);
362 }
363 #endif
364 }
365
366 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
367 {
368 #ifdef USE_UBLIO
369 ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
370 if (result >= 0)
371 dev->pos += size;
372 return result;
373 #else
374 return read(dev->fd, buffer, size);
375 #endif
376 }
377
378 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
379 {
380 #ifdef USE_UBLIO
381 ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
382 if (result >= 0)
383 dev->pos += size;
384 return result;
385 #else
386 return write(dev->fd, buffer, size);
387 #endif
388 }
389
390 ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
391 off_t offset)
392 {
393 #ifdef USE_UBLIO
394 return ublio_pread(dev->ufh, buffer, size, offset);
395 #else
396 return pread(dev->fd, buffer, size, offset);
397 #endif
398 }
399
400 ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
401 off_t offset)
402 {
403 #ifdef USE_UBLIO
404 return ublio_pwrite(dev->ufh, buffer, size, offset);
405 #else
406 return pwrite(dev->fd, buffer, size, offset);
407 #endif
408 }
409
410 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
411 void* buffer, size_t size, off_t offset)
412 {
413 cluster_t cluster;
414 char* bufp = buffer;
415 off_t lsize, loffset, remainder;
416
417 if (offset >= node->size)
418 return 0;
419 if (size == 0)
420 return 0;
421
422 cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
423 if (CLUSTER_INVALID(*ef->sb, cluster))
424 {
425 exfat_error("invalid cluster 0x%x while reading", cluster);
426 return -EIO;
427 }
428
429 loffset = offset % CLUSTER_SIZE(*ef->sb);
430 remainder = MIN(size, node->size - offset);
431 while (remainder > 0)
432 {
433 if (CLUSTER_INVALID(*ef->sb, cluster))
434 {
435 exfat_error("invalid cluster 0x%x while reading", cluster);
436 return -EIO;
437 }
438 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
439 if (exfat_pread(ef->dev, bufp, lsize,
440 exfat_c2o(ef, cluster) + loffset) < 0)
441 {
442 exfat_error("failed to read cluster %#x", cluster);
443 return -EIO;
444 }
445 bufp += lsize;
446 loffset = 0;
447 remainder -= lsize;
448 cluster = exfat_next_cluster(ef, node, cluster);
449 }
450 if (!(node->attrib & EXFAT_ATTRIB_DIR) && !ef->ro && !ef->noatime)
451 exfat_update_atime(node);
452 return MIN(size, node->size - offset) - remainder;
453 }
454
455 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
456 const void* buffer, size_t size, off_t offset)
457 {
458 int rc;
459 cluster_t cluster;
460 const char* bufp = buffer;
461 off_t lsize, loffset, remainder;
462
463 if (offset > node->size)
464 {
465 rc = exfat_truncate(ef, node, offset, true);
466 if (rc != 0)
467 return rc;
468 }
469 if (offset + size > node->size)
470 {
471 rc = exfat_truncate(ef, node, offset + size, false);
472 if (rc != 0)
473 return rc;
474 }
475 if (size == 0)
476 return 0;
477
478 cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
479 if (CLUSTER_INVALID(*ef->sb, cluster))
480 {
481 exfat_error("invalid cluster 0x%x while writing", cluster);
482 return -EIO;
483 }
484
485 loffset = offset % CLUSTER_SIZE(*ef->sb);
486 remainder = size;
487 while (remainder > 0)
488 {
489 if (CLUSTER_INVALID(*ef->sb, cluster))
490 {
491 exfat_error("invalid cluster 0x%x while writing", cluster);
492 return -EIO;
493 }
494 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
495 if (exfat_pwrite(ef->dev, bufp, lsize,
496 exfat_c2o(ef, cluster) + loffset) < 0)
497 {
498 exfat_error("failed to write cluster %#x", cluster);
499 return -EIO;
500 }
501 bufp += lsize;
502 loffset = 0;
503 remainder -= lsize;
504 cluster = exfat_next_cluster(ef, node, cluster);
505 }
506 if (!(node->attrib & EXFAT_ATTRIB_DIR))
507 /* directory's mtime should be updated by the caller only when it
508 creates or removes something in this directory */
509 exfat_update_mtime(node);
510 return size - remainder;
511 }