]>
glassweightruler.freedombox.rocks Git - Ventoy.git/blob - LinuxGUI/Ventoy2Disk/Lib/exfat/src/libexfat/io.c
3 exFAT file system implementation library.
5 Free exFAT implementation.
6 Copyright (C) 2010-2018 Andrew Nayenko
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.
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.
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.
25 #include <sys/types.h>
31 #if defined(__APPLE__)
33 #elif defined(__OpenBSD__)
34 #include <sys/param.h>
35 #include <sys/disklabel.h>
37 #include <sys/ioctl.h>
39 #include <sys/mount.h>
50 off_t size
; /* in bytes */
53 ublio_filehandle_t ufh
;
57 int g_vtoy_exfat_disk_fd
= -1;
58 uint64_t g_vtoy_exfat_part_size
= 0;
60 static bool is_open(int fd
)
62 return fcntl(fd
, F_GETFD
) != -1;
65 static int open_ro(const char* spec
)
67 return open(spec
, O_RDONLY
);
70 static int open_rw(const char* spec
)
72 int fd
= open(spec
, O_RDWR
);
77 This ioctl is needed because after "blockdev --setro" kernel still
78 allows to open the device in read-write mode but fails writes.
80 if (fd
!= -1 && ioctl(fd
, BLKROGET
, &ro
) == 0 && ro
)
90 struct exfat_dev
* exfat_open(const char* spec
, enum exfat_mode mode
)
92 struct exfat_dev
* dev
;
95 struct ublio_param up
;
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
))
108 /* we don't need those descriptors, let them leak */
109 if (open("/dev/null", O_RDWR
) == -1)
111 exfat_error("failed to open /dev/null");
116 dev
= malloc(sizeof(struct exfat_dev
));
119 exfat_error("failed to allocate memory for device structure");
126 dev
->fd
= g_vtoy_exfat_disk_fd
< 0 ? open_ro(spec
) : g_vtoy_exfat_disk_fd
;
130 exfat_error("failed to open '%s' in read-only mode: %s", spec
,
134 dev
->mode
= EXFAT_MODE_RO
;
137 dev
->fd
= g_vtoy_exfat_disk_fd
< 0 ? open_rw(spec
) : g_vtoy_exfat_disk_fd
;
141 exfat_error("failed to open '%s' in read-write mode: %s", spec
,
145 dev
->mode
= EXFAT_MODE_RW
;
148 dev
->fd
= g_vtoy_exfat_disk_fd
< 0 ? open_rw(spec
) : g_vtoy_exfat_disk_fd
;
151 dev
->mode
= EXFAT_MODE_RW
;
154 dev
->fd
= g_vtoy_exfat_disk_fd
< 0 ? open_ro(spec
) : g_vtoy_exfat_disk_fd
;
157 dev
->mode
= EXFAT_MODE_RO
;
158 exfat_warn("'%s' is write-protected, mounting read-only", spec
);
162 exfat_error("failed to open '%s': %s", spec
, strerror(errno
));
166 if (fstat(dev
->fd
, &stbuf
) != 0)
170 exfat_error("failed to fstat '%s'", spec
);
173 if (!S_ISBLK(stbuf
.st_mode
) &&
174 !S_ISCHR(stbuf
.st_mode
) &&
175 !S_ISREG(stbuf
.st_mode
))
179 exfat_error("'%s' is neither a device, nor a regular file", spec
);
183 #if defined(__APPLE__)
184 if (!S_ISREG(stbuf
.st_mode
))
186 uint32_t block_size
= 0;
189 if (ioctl(dev
->fd
, DKIOCGETBLOCKSIZE
, &block_size
) != 0)
193 exfat_error("failed to get block size");
196 if (ioctl(dev
->fd
, DKIOCGETBLOCKCOUNT
, &blocks
) != 0)
200 exfat_error("failed to get blocks count");
203 dev
->size
= blocks
* block_size
;
206 #elif defined(__OpenBSD__)
207 if (!S_ISREG(stbuf
.st_mode
))
209 struct disklabel lab
;
210 struct partition
* pp
;
213 if (ioctl(dev
->fd
, DIOCGDINFO
, &lab
) == -1)
217 exfat_error("failed to get disklabel");
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
;
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)");
234 /* works for Linux, FreeBSD, Solaris */
235 dev
->size
= exfat_seek(dev
, 0, SEEK_END
);
240 exfat_error("failed to get size of '%s'", spec
);
243 if (exfat_seek(dev
, 0, SEEK_SET
) == -1)
247 exfat_error("failed to seek to the beginning of '%s'", spec
);
253 memset(&up
, 0, sizeof(struct ublio_param
));
254 up
.up_blocksize
= 256 * 1024;
257 up
.up_priv
= &dev
->fd
;
260 dev
->ufh
= ublio_open(&up
);
261 if (dev
->ufh
== NULL
)
265 exfat_error("failed to initialize ublio");
273 int exfat_close(struct exfat_dev
* dev
)
278 if (ublio_close(dev
->ufh
) != 0)
280 exfat_error("failed to close ublio");
284 if (dev
->fd
!= g_vtoy_exfat_disk_fd
)
286 if (close(dev
->fd
) != 0)
288 exfat_error("failed to close device: %s", strerror(errno
));
297 int exfat_fsync(struct exfat_dev
* dev
)
302 if (ublio_fsync(dev
->ufh
) != 0)
304 exfat_error("ublio fsync failed");
308 if (fsync(dev
->fd
) != 0)
310 exfat_error("fsync failed: %s", strerror(errno
));
316 enum exfat_mode
exfat_get_mode(const struct exfat_dev
* dev
)
322 off_t
exfat_get_size(const struct exfat_dev
* dev
)
327 off_t
exfat_seek(struct exfat_dev
* dev
, off_t offset
, int whence
)
330 /* XXX SEEK_CUR will be handled incorrectly */
331 return dev
->pos
= lseek(dev
->fd
, offset
, whence
);
334 if (SEEK_SET
== whence
)
336 if (offset
> g_vtoy_exfat_part_size
)
341 lseek(dev
->fd
, 512 * 2048 + offset
, SEEK_SET
);
344 else if (SEEK_END
== whence
)
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
;
354 exfat_error("Invalid SEEK_END offset %llu", (unsigned long long)offset
);
360 exfat_error("Invalid seek whence %d", whence
);
361 return lseek(dev
->fd
, offset
, whence
);
366 ssize_t
exfat_read(struct exfat_dev
* dev
, void* buffer
, size_t size
)
369 ssize_t result
= ublio_pread(dev
->ufh
, buffer
, size
, dev
->pos
);
374 return read(dev
->fd
, buffer
, size
);
378 ssize_t
exfat_write(struct exfat_dev
* dev
, const void* buffer
, size_t size
)
381 ssize_t result
= ublio_pwrite(dev
->ufh
, buffer
, size
, dev
->pos
);
386 return write(dev
->fd
, buffer
, size
);
390 ssize_t
exfat_pread(struct exfat_dev
* dev
, void* buffer
, size_t size
,
394 return ublio_pread(dev
->ufh
, buffer
, size
, offset
);
396 return pread(dev
->fd
, buffer
, size
, offset
);
400 ssize_t
exfat_pwrite(struct exfat_dev
* dev
, const void* buffer
, size_t size
,
404 return ublio_pwrite(dev
->ufh
, buffer
, size
, offset
);
406 return pwrite(dev
->fd
, buffer
, size
, offset
);
410 ssize_t
exfat_generic_pread(const struct exfat
* ef
, struct exfat_node
* node
,
411 void* buffer
, size_t size
, off_t offset
)
415 off_t lsize
, loffset
, remainder
;
417 if (offset
>= node
->size
)
422 cluster
= exfat_advance_cluster(ef
, node
, offset
/ CLUSTER_SIZE(*ef
->sb
));
423 if (CLUSTER_INVALID(*ef
->sb
, cluster
))
425 exfat_error("invalid cluster 0x%x while reading", cluster
);
429 loffset
= offset
% CLUSTER_SIZE(*ef
->sb
);
430 remainder
= MIN(size
, node
->size
- offset
);
431 while (remainder
> 0)
433 if (CLUSTER_INVALID(*ef
->sb
, cluster
))
435 exfat_error("invalid cluster 0x%x while reading", cluster
);
438 lsize
= MIN(CLUSTER_SIZE(*ef
->sb
) - loffset
, remainder
);
439 if (exfat_pread(ef
->dev
, bufp
, lsize
,
440 exfat_c2o(ef
, cluster
) + loffset
) < 0)
442 exfat_error("failed to read cluster %#x", cluster
);
448 cluster
= exfat_next_cluster(ef
, node
, cluster
);
450 if (!(node
->attrib
& EXFAT_ATTRIB_DIR
) && !ef
->ro
&& !ef
->noatime
)
451 exfat_update_atime(node
);
452 return MIN(size
, node
->size
- offset
) - remainder
;
455 ssize_t
exfat_generic_pwrite(struct exfat
* ef
, struct exfat_node
* node
,
456 const void* buffer
, size_t size
, off_t offset
)
460 const char* bufp
= buffer
;
461 off_t lsize
, loffset
, remainder
;
463 if (offset
> node
->size
)
465 rc
= exfat_truncate(ef
, node
, offset
, true);
469 if (offset
+ size
> node
->size
)
471 rc
= exfat_truncate(ef
, node
, offset
+ size
, false);
478 cluster
= exfat_advance_cluster(ef
, node
, offset
/ CLUSTER_SIZE(*ef
->sb
));
479 if (CLUSTER_INVALID(*ef
->sb
, cluster
))
481 exfat_error("invalid cluster 0x%x while writing", cluster
);
485 loffset
= offset
% CLUSTER_SIZE(*ef
->sb
);
487 while (remainder
> 0)
489 if (CLUSTER_INVALID(*ef
->sb
, cluster
))
491 exfat_error("invalid cluster 0x%x while writing", cluster
);
494 lsize
= MIN(CLUSTER_SIZE(*ef
->sb
) - loffset
, remainder
);
495 if (exfat_pwrite(ef
->dev
, bufp
, lsize
,
496 exfat_c2o(ef
, cluster
) + loffset
) < 0)
498 exfat_error("failed to write cluster %#x", cluster
);
504 cluster
= exfat_next_cluster(ef
, node
, cluster
);
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
;