├── .gitignore ├── LICENCE ├── Makefile ├── README ├── ufat_internal.h ├── ufat_file.c ├── ufat_dir.c ├── ufat_mkfs.c ├── ufat_ent.c ├── ufat.c ├── ufat.h └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | ufat 3 | .*.swp 4 | *~ 5 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2012 TracMap Holdings Ltd 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # uFAT -- small flexible VFAT implementation 2 | # Copyright (C) 2012 TracMap Holdings Ltd 3 | # 4 | # Author: Daniel Beer , www.dlbeer.co.nz 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are 8 | # met: 9 | # 10 | # 1. Redistributions of source code must retain the above copyright 11 | # notice, this list of conditions and the following disclaimer. 12 | # 13 | # 2. Redistributions in binary form must reproduce the above copyright 14 | # notice, this list of conditions and the following disclaimer in the 15 | # documentation and/or other materials provided with the distribution. 16 | # 17 | # 3. Neither the name of the copyright holder nor the names of its 18 | # contributors may be used to endorse or promote products derived from 19 | # this software without specific prior written permission. 20 | # 21 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | 33 | CC ?= gcc 34 | UFAT_CFLAGS = -O1 -Wall -Wextra -Wshadow -Wpedantic -ggdb 35 | 36 | all: ufat 37 | 38 | ufat: ufat.o ufat_dir.o ufat_file.o ufat_ent.o ufat_mkfs.o main.o 39 | $(CC) -o $@ $^ 40 | 41 | %.o: %.c 42 | $(CC) $(CFLAGS) $(UFAT_CFLAGS) -o $*.o -c $*.c 43 | 44 | clean: 45 | rm -f *.o 46 | rm -f ufat 47 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | uFAT 2 | ==== 3 | 4 | uFAT is small but feature-complete VFAT/FAT32 implementation. It 5 | supports all basic filesystem operations and has minimal memory 6 | requirements. Its key features are: 7 | 8 | * No global data: can be used to access multiple filesystems 9 | simultaneously without issue. 10 | 11 | * Small memory footprint: only a few kB required for metadata 12 | caching and long filename parsing (the cache size is configurable 13 | and can be as low as 512 bytes). 14 | 15 | * No heap allocation is used. 16 | 17 | * Highly portable: no external dependencies except for a few 18 | easily-implementable ANSI C functions (memcpy, memset, atoi). 19 | 20 | * Unicode filenames are supported (via translation to and from UTF-8 21 | for function call arguments). 22 | 23 | * Supports reading, writing and creating FAT12, FAT16 and FAT32, 24 | including long filenames. 25 | 26 | Using uFAT as a library 27 | ----------------------- 28 | 29 | The interface to the underlying block device is abstract and specified 30 | though a structure containing some key parameters and access functions: 31 | 32 | typedef unsigned long long ufat_block_t; 33 | 34 | struct ufat_device { 35 | unsigned int log2_block_size; 36 | int (*read)(const struct ufat_device *dev, ufat_block_t start, 37 | ufat_block_t count, void *buffer); 38 | int (*write)(const struct ufat_device *dev, ufat_block_t start, 39 | ufat_block_t count, const void *buffer); 40 | }; 41 | 42 | A pointer to the ``struct ufat_device`` is passed with each call to 43 | ``read`` or ``write``, so driver-private data can be kept by embedding 44 | the structure in a larger structure. 45 | 46 | All ``ufat_block_t`` arguments are block indices relative to the 47 | beginning of the partition. ``log2_block_size`` should be the base-2 48 | logarithm of the block size (that is, ``1 << log2_block_size`` is the 49 | actual block size in bytes). Having filled out a ``struct 50 | ufat_device`` for the partition/device you want to access, Use 51 | ``ufat_open``/``ufat_close`` to open and close the filesystem: 52 | 53 | struct ufat_device dev; 54 | struct ufat uf; 55 | int err; 56 | 57 | dev.log2_block_size = 9; 58 | dev.read = my_read; 59 | dev.write = my_write; 60 | 61 | err = ufat_open(&uf, &dev); 62 | if (err < 0) { 63 | fprintf(stderr, "Error: %s\n", ufat_strerror(err)); 64 | return -1; 65 | } 66 | 67 | /* ... */ 68 | 69 | ufat_close(&uf); 70 | 71 | No memory is allocated when a filesystem is opened, but ``ufat_close`` 72 | must be called to flush caches if the filesystem has been modified. 73 | 74 | There are three basic objects used by the filesystem implementation: 75 | 76 | ``struct ufat_dirent`` 77 | 78 | ~ This represents a directory entry. It holds information on a file or 79 | directory and contains the location of the entry on the underlying 80 | device, attributes and file size (for regular files). 81 | 82 | ``struct ufat_directory`` 83 | 84 | ~ This structure is a directory iterator. There are two ways to 85 | construct this object: either by opening the root directory, or by 86 | opening a subdirectory represented by a ``ufat_dirent`` using 87 | ``ufat_open_subdir``. Iterating through the contents of a 88 | directory returns a series of ``ufat_dirent`` structures. 89 | 90 | ``struct ufat_file`` 91 | 92 | ~ This structure represents an open file. It is constructed from a 93 | ``ufat_dirent`` via a call to ``ufat_open_file``. 94 | 95 | Functions for manipulating the filesystem and the associated objects 96 | are detailed in the ``ufat.h`` header file. Examples may be found in 97 | the included image-manipulation program (``main.c`` in the source 98 | distribution). 99 | 100 | All functions with ``int`` return types will return 0 on success or a 101 | negative error code if there is a problem with the underlying 102 | filesystem or device. These error codes can be translated into 103 | human-readable strings with ``ufat_strerror``. Error codes are 104 | declared as values for the enum ``ufat_error_t``. 105 | 106 | Directories may be traversed by a series of 107 | ``ufat_open_subdir``/``ufat_dir_find`` calls. For cases where a full 108 | slash-separated path is known, the following function is useful: 109 | 110 | int ufat_dir_find_path(struct ufat_directory *dir, 111 | const char *path, struct ufat_dirent *inf, 112 | const char **path_out); 113 | 114 | Its arguments are: 115 | 116 | ``dir`` 117 | 118 | ~ An open directory from which to start the search. This will in 119 | most cases be the root directory, but it can be any subdirectory, 120 | and the path given will be treated as relative to the initial 121 | directory. 122 | 123 | ``path`` 124 | 125 | ~ A slash-separated path, relative to the starting directory. Path 126 | components may be separated by either a forward-slash or a 127 | backslash. This path should be UTF-8 encoded. 128 | 129 | ``inf`` 130 | 131 | ~ A pointer to a ``ufat_dirent`` which will be filled out with the 132 | details of the file/directory found. 133 | 134 | ``path_out`` 135 | 136 | ~ A pointer to the remainder of the path not processed if the file 137 | could not be found. 138 | 139 | If an IO or filesystem error occurs, a negative error code is 140 | returned. When the given path identifies an existing file or 141 | directory, the structure pointed to ``inf`` is filled out and the 142 | function returns 0. 143 | 144 | If the given path doesn't name an existing file or directory, 1 is 145 | returned. In this case, ``inf`` is not filled out, but the pointer 146 | pointed to by ``path_out`` will be filled out with a pointer to the 147 | remainder of ``path`` which was not processed. 148 | 149 | In all cases, ``dir`` will be reinitialized to be an iterator for the 150 | directory where the search terminated. 151 | 152 | Copyright 153 | --------- 154 | 155 | Copyright (C) 2012 TracMap Holdings Ltd 156 | 157 | Redistribution and use in source and binary forms, with or without 158 | modification, are permitted provided that the following conditions are 159 | met: 160 | 161 | 1. Redistributions of source code must retain the above copyright 162 | notice, this list of conditions and the following disclaimer. 163 | 164 | 2. Redistributions in binary form must reproduce the above copyright 165 | notice, this list of conditions and the following disclaimer in the 166 | documentation and/or other materials provided with the distribution. 167 | 168 | 3. Neither the name of the copyright holder nor the names of its 169 | contributors may be used to endorse or promote products derived from 170 | this software without specific prior written permission. 171 | 172 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 173 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 174 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 175 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 176 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 177 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 178 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 179 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 180 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 181 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 182 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 183 | -------------------------------------------------------------------------------- /ufat_internal.h: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef UFAT_INTERNAL_H_ 35 | #define UFAT_INTERNAL_H_ 36 | 37 | #define UFAT_CLUSTER_MASK 0x0fffffff 38 | #define UFAT_DIRENT_SIZE 32 39 | 40 | #define UFAT_MAX_FAT12 0xff6 41 | #define UFAT_MAX_FAT16 0xfff6 42 | 43 | static inline uint16_t r16(const uint8_t *offset) 44 | { 45 | const uint16_t l = offset[0]; 46 | const uint16_t h = offset[1]; 47 | 48 | return (h << 8) | l; 49 | } 50 | 51 | static inline uint32_t r32(const uint8_t *offset) 52 | { 53 | const uint32_t l = r16(offset); 54 | const uint32_t h = r16(offset + 2); 55 | 56 | return (h << 16) | l; 57 | } 58 | 59 | static inline void w16(uint8_t *offset, uint16_t value) 60 | { 61 | offset[0] = value & 0xff; 62 | offset[1] = value >> 8; 63 | } 64 | 65 | static inline void w32(uint8_t *offset, uint32_t value) 66 | { 67 | w16(offset, value & 0xffff); 68 | w16(offset + 2, value >> 16); 69 | } 70 | 71 | static inline ufat_block_t cluster_to_block(const struct ufat_bpb *bpb, 72 | ufat_cluster_t c) 73 | { 74 | return ((c - 2) << bpb->log2_blocks_per_cluster) + 75 | bpb->cluster_start; 76 | } 77 | 78 | static inline ufat_cluster_t block_to_cluster(const struct ufat_bpb *bpb, 79 | ufat_block_t b) 80 | { 81 | return ((b - bpb->cluster_start) >> bpb->log2_blocks_per_cluster) + 2; 82 | } 83 | 84 | /* Block IO via internal cache */ 85 | 86 | /** 87 | * \brief Opens a block via cache. 88 | * 89 | * \pre `uf` is a valid pointer. 90 | * \pre The filesystem pointed by `uf` is opened. 91 | * 92 | * \param [in] uf is a pointer to the filesystem 93 | * \param [in] blk_index is the index of block which should be opened 94 | * \param [in] skip_read selects the behavior when block is not present in the 95 | * cache: 96 | * - 0 - current contents of the block are read from the device; 97 | * - not 0 - read operation is skipped and the cached contents are just zeroed - 98 | * this is useful when the whole block is going to be overwritten anyway; 99 | * 100 | * \return cache index on success, negative error code (`ufat_error_t`) 101 | * otherwise 102 | */ 103 | 104 | int ufat_cache_open(struct ufat *uf, ufat_block_t blk_index, int skip_read); 105 | 106 | /** 107 | * \brief Evicts (flushes) cached blocks which overlap with given range. 108 | * 109 | * \pre `uf` is a valid pointer. 110 | * \pre The filesystem pointed by `uf` is opened. 111 | * 112 | * \param [in] uf is a pointer to the filesystem 113 | * \param [in] start is the index of starting block of the range 114 | * \param [in] count is the number of blocks in the range 115 | * 116 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 117 | */ 118 | 119 | int ufat_cache_evict(struct ufat *uf, ufat_block_t start, ufat_block_t count); 120 | 121 | /** 122 | * \brief Invalidates (drops) cached blocks which overlap with given range. 123 | * 124 | * \pre `uf` is a valid pointer. 125 | * \pre The filesystem pointed by `uf` is opened. 126 | * 127 | * \param [in] uf is a pointer to the filesystem 128 | * \param [in] start is the index of starting block of the range 129 | * \param [in] count is the number of blocks in the range 130 | */ 131 | 132 | void ufat_cache_invalidate(struct ufat *uf, ufat_block_t start, 133 | ufat_block_t count); 134 | 135 | static inline void ufat_cache_write(struct ufat *uf, unsigned int cache_index) 136 | { 137 | uf->stat.cache_write++; 138 | uf->cache_desc[cache_index].flags |= UFAT_CACHE_FLAG_DIRTY; 139 | } 140 | 141 | static inline uint8_t *ufat_cache_data(struct ufat *uf, 142 | unsigned int cache_index) 143 | { 144 | return uf->cache_data + (cache_index << uf->dev->log2_block_size); 145 | } 146 | 147 | /* FAT entry IO */ 148 | int ufat_read_fat(struct ufat *uf, ufat_cluster_t index, 149 | ufat_cluster_t *out); 150 | int ufat_write_fat(struct ufat *uf, ufat_cluster_t index, 151 | ufat_cluster_t in); 152 | 153 | /* High-level FAT operations */ 154 | int ufat_free_chain(struct ufat *uf, ufat_cluster_t start); 155 | int ufat_alloc_chain(struct ufat *uf, unsigned int count, ufat_cluster_t *out); 156 | 157 | /* LFN handling */ 158 | struct ufat_lfn_parser { 159 | ufat_block_t start_block; 160 | unsigned int start_pos; 161 | 162 | uint8_t short_checksum; 163 | 164 | int len; 165 | int seq; 166 | uint16_t buf[UFAT_LFN_MAX_CHARS]; 167 | }; 168 | 169 | static inline void ufat_lfn_reset(struct ufat_lfn_parser *s) 170 | { 171 | s->seq = -1; 172 | } 173 | 174 | static inline int ufat_lfn_ok(const struct ufat_lfn_parser *s) 175 | { 176 | return s->seq == 0; 177 | } 178 | 179 | void ufat_lfn_parse(struct ufat_lfn_parser *s, const uint8_t *data, 180 | ufat_block_t blk, unsigned int pos); 181 | int ufat_lfn_is_legal(const char *name); 182 | void ufat_lfn_pack_fragment(const uint16_t *ucs, int seq, int is_first, 183 | uint8_t *data, uint8_t checksum); 184 | 185 | /* Raw dirent IO */ 186 | int ufat_write_raw_dirent(struct ufat_directory *dir, 187 | const uint8_t *data, unsigned int len); 188 | int ufat_read_raw_dirent(struct ufat_directory *dir, uint8_t *data); 189 | int ufat_advance_raw_dirent(struct ufat_directory *dir, int can_alloc); 190 | int ufat_allocate_raw_dirent(struct ufat_directory *dir, unsigned int count); 191 | int ufat_init_dirent_cluster(struct ufat *uf, ufat_cluster_t c); 192 | 193 | /* Dirent parsing/packing */ 194 | void ufat_parse_dirent(ufat_fat_type_t type, 195 | const uint8_t *data, struct ufat_dirent *inf); 196 | void ufat_pack_dirent(const struct ufat_dirent *ent, uint8_t *data); 197 | 198 | /* Charset conversion */ 199 | int ufat_ucs2_to_utf8(const uint16_t *src, int src_len, 200 | char *dst, int dst_len); 201 | int ufat_utf8_to_ucs2(const char *src, uint16_t *dst); 202 | 203 | /* Short name functions */ 204 | void ufat_short_first(const char *long_name, 205 | char *short_name, char *ext_text); 206 | void ufat_short_next(char *short_name); 207 | int ufat_short_exists(struct ufat_directory *dir, const char *short_name, 208 | const char *short_ext); 209 | uint8_t ufat_short_checksum(const char *short_name, const char *short_ext); 210 | int ufat_format_short(const char *name, const char *ext, 211 | char *out, int max_len); 212 | 213 | /* Long name comparison. 214 | * 215 | * Names are compared without regard to case. If component_only is set, 216 | * then any '/' or '\\' character in search_name is treated as a 217 | * terminator. 218 | * 219 | * Returns the length of the match, or -1 if the names don't match. 220 | */ 221 | int ufat_compare_name(const char *search_name, const char *dir_name, 222 | int component_only); 223 | 224 | #endif 225 | -------------------------------------------------------------------------------- /ufat_file.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include "ufat.h" 36 | #include "ufat_internal.h" 37 | 38 | int ufat_open_file(struct ufat *uf, struct ufat_file *f, 39 | const struct ufat_dirent *ent) 40 | { 41 | if (ent->attributes & UFAT_ATTR_DIRECTORY) 42 | return -UFAT_ERR_NOT_FILE; 43 | 44 | f->uf = uf; 45 | f->dirent_block = ent->dirent_block; 46 | f->dirent_pos = ent->dirent_pos; 47 | f->start = ent->first_cluster; 48 | f->file_size = ent->file_size; 49 | f->prev_cluster = 0; 50 | f->cur_cluster = f->start; 51 | f->cur_pos = 0; 52 | 53 | return 0; 54 | } 55 | 56 | void ufat_file_rewind(struct ufat_file *f) 57 | { 58 | f->prev_cluster = 0; 59 | f->cur_cluster = f->start; 60 | f->cur_pos = 0; 61 | } 62 | 63 | static int advance_ptr(struct ufat_file *f, ufat_size_t nbytes) 64 | { 65 | const unsigned int log2_cluster_size = 66 | f->uf->dev->log2_block_size + 67 | f->uf->bpb.log2_blocks_per_cluster; 68 | ufat_size_t end_pos; 69 | unsigned int nclusters; 70 | ufat_cluster_t c = f->cur_cluster; 71 | ufat_cluster_t prev = f->prev_cluster; 72 | 73 | end_pos = f->cur_pos + nbytes; 74 | 75 | nclusters = (end_pos >> log2_cluster_size) - 76 | (f->cur_pos >> log2_cluster_size); 77 | 78 | while (nclusters) { 79 | ufat_cluster_t next; 80 | int i; 81 | 82 | i = ufat_read_fat(f->uf, c, &next); 83 | if (i < 0) 84 | return i; 85 | 86 | prev = c; 87 | c = next; 88 | nclusters--; 89 | } 90 | 91 | f->prev_cluster = prev; 92 | f->cur_cluster = c; 93 | f->cur_pos += nbytes; 94 | 95 | return 0; 96 | } 97 | 98 | int ufat_file_advance(struct ufat_file *f, ufat_size_t nbytes) 99 | { 100 | if (nbytes > f->file_size - f->cur_pos) 101 | nbytes = f->file_size - f->cur_pos; 102 | 103 | return advance_ptr(f, nbytes); 104 | } 105 | 106 | static int read_block_fragment(struct ufat_file *f, char *buf, ufat_size_t size) 107 | { 108 | const struct ufat_bpb *bpb = &f->uf->bpb; 109 | const unsigned int log2_block_size = f->uf->dev->log2_block_size; 110 | const unsigned int block_size = 1 << log2_block_size; 111 | const unsigned int offset = f->cur_pos & (block_size - 1); 112 | const unsigned int remainder = block_size - offset; 113 | const ufat_block_t cur_block = 114 | cluster_to_block(bpb, f->cur_cluster) + 115 | ((f->cur_pos >> log2_block_size) & 116 | ((1 << bpb->log2_blocks_per_cluster) - 1)); 117 | int i; 118 | 119 | if (size > remainder) 120 | size = remainder; 121 | size &= (block_size - 1); 122 | if (!size) 123 | return 0; 124 | 125 | if (!UFAT_CLUSTER_IS_PTR(f->cur_cluster)) 126 | return -UFAT_ERR_INVALID_CLUSTER; 127 | 128 | i = ufat_cache_open(f->uf, cur_block, 0); 129 | if (i < 0) 130 | return i; 131 | 132 | memcpy(buf, ufat_cache_data(f->uf, i) + offset, size); 133 | i = advance_ptr(f, size); 134 | if (i < 0) 135 | return i; 136 | 137 | return size; 138 | } 139 | 140 | static int read_blocks(struct ufat_file *f, char *buf, ufat_size_t size) 141 | { 142 | struct ufat *uf = f->uf; 143 | const struct ufat_bpb *bpb = &uf->bpb; 144 | const unsigned int log2_block_size = uf->dev->log2_block_size; 145 | const unsigned int blocks_per_cluster = 146 | 1 << bpb->log2_blocks_per_cluster; 147 | const unsigned int block_offset = 148 | (f->cur_pos >> log2_block_size) & (blocks_per_cluster - 1); 149 | const unsigned int block_remainder = 150 | blocks_per_cluster - block_offset; 151 | ufat_block_t starting_block; 152 | unsigned int requested_blocks = size >> log2_block_size; 153 | int i; 154 | 155 | if (requested_blocks > block_remainder) 156 | requested_blocks = block_remainder; 157 | if (!requested_blocks) 158 | return 0; 159 | 160 | if (!UFAT_CLUSTER_IS_PTR(f->cur_cluster)) 161 | return -UFAT_ERR_INVALID_CLUSTER; 162 | 163 | /* We're reading contiguous whole blocks, so we can bypass the 164 | * cache and perform a single large read. 165 | */ 166 | starting_block = cluster_to_block(bpb, f->cur_cluster) + block_offset; 167 | i = ufat_cache_evict(uf, starting_block, requested_blocks); 168 | if (i < 0) 169 | return i; 170 | 171 | i = uf->dev->read(uf->dev, starting_block, requested_blocks, buf); 172 | if (i < 0) 173 | return -UFAT_ERR_IO; 174 | 175 | uf->stat.read++; 176 | uf->stat.read_blocks += requested_blocks; 177 | 178 | i = advance_ptr(f, requested_blocks << log2_block_size); 179 | if (i < 0) 180 | return i; 181 | 182 | return requested_blocks << log2_block_size; 183 | } 184 | 185 | int ufat_file_read(struct ufat_file *f, void *buf, ufat_size_t size) 186 | { 187 | ufat_size_t total; 188 | int len; 189 | 190 | if (size > f->file_size - f->cur_pos) 191 | size = f->file_size - f->cur_pos; 192 | total = size; 193 | 194 | /* Read partial block ends */ 195 | len = read_block_fragment(f, buf, size); 196 | if (len < 0) 197 | return len; 198 | 199 | buf = (char*)buf + len; 200 | size -= len; 201 | 202 | /* Read contiguous blocks within a cluster */ 203 | for (;;) { 204 | len = read_blocks(f, buf, size); 205 | 206 | if (len < 0) 207 | return len; 208 | 209 | if (!len) 210 | break; 211 | 212 | buf = (char*)buf + len; 213 | size -= len; 214 | } 215 | 216 | /* Read remaining block fragment */ 217 | len = read_block_fragment(f, buf, size); 218 | if (len < 0) 219 | return len; 220 | 221 | return total; 222 | } 223 | 224 | static int set_size(struct ufat_file *f, ufat_size_t s) 225 | { 226 | int idx = ufat_cache_open(f->uf, f->dirent_block, 0); 227 | 228 | if (idx < 0) 229 | return idx; 230 | 231 | ufat_cache_write(f->uf, idx); 232 | w32(ufat_cache_data(f->uf, idx) + 233 | f->dirent_pos * UFAT_DIRENT_SIZE + 0x1c, s); 234 | 235 | f->file_size = s; 236 | return 0; 237 | } 238 | 239 | static int set_start(struct ufat_file *f, ufat_cluster_t s) 240 | { 241 | int idx = ufat_cache_open(f->uf, f->dirent_block, 0); 242 | uint8_t *data; 243 | 244 | if (idx < 0) 245 | return idx; 246 | 247 | ufat_cache_write(f->uf, idx); 248 | data = ufat_cache_data(f->uf, idx) + f->dirent_pos * UFAT_DIRENT_SIZE; 249 | w16(data + 0x14, s >> 16); 250 | w16(data + 0x1a, s & 0xffff); 251 | 252 | f->start = s; 253 | return 0; 254 | } 255 | 256 | static int ensure_room(struct ufat_file *f) 257 | { 258 | ufat_cluster_t c; 259 | int err; 260 | 261 | if (UFAT_CLUSTER_IS_PTR(f->cur_cluster)) 262 | return 0; 263 | 264 | err = ufat_alloc_chain(f->uf, 1, &c); 265 | if (err < 0) 266 | return err; 267 | 268 | if (UFAT_CLUSTER_IS_PTR(f->prev_cluster)) 269 | err = ufat_write_fat(f->uf, f->prev_cluster, c); 270 | else 271 | err = set_start(f, c); 272 | 273 | if (err < 0) { 274 | ufat_free_chain(f->uf, c); 275 | return err; 276 | } 277 | 278 | f->cur_cluster = c; 279 | return 0; 280 | } 281 | 282 | static int write_block_fragment(struct ufat_file *f, const char *buf, 283 | ufat_size_t size) 284 | { 285 | const struct ufat_bpb *bpb = &f->uf->bpb; 286 | const unsigned int log2_block_size = f->uf->dev->log2_block_size; 287 | const unsigned int block_size = 1 << log2_block_size; 288 | const unsigned int offset = f->cur_pos & (block_size - 1); 289 | const unsigned int remainder = block_size - offset; 290 | ufat_block_t cur_block; 291 | int i; 292 | 293 | if (size > remainder) 294 | size = remainder; 295 | size &= (block_size - 1); 296 | if (!size) 297 | return 0; 298 | 299 | i = ensure_room(f); 300 | if (i < 0) 301 | return i; 302 | 303 | cur_block = 304 | cluster_to_block(bpb, f->cur_cluster) + 305 | ((f->cur_pos >> log2_block_size) & 306 | ((1 << bpb->log2_blocks_per_cluster) - 1)); 307 | 308 | i = ufat_cache_open(f->uf, cur_block, 0); 309 | if (i < 0) 310 | return i; 311 | 312 | ufat_cache_write(f->uf, i); 313 | memcpy(ufat_cache_data(f->uf, i) + offset, buf, size); 314 | 315 | i = advance_ptr(f, size); 316 | if (i < 0) 317 | return i; 318 | 319 | return size; 320 | } 321 | 322 | static int write_blocks(struct ufat_file *f, const char *buf, ufat_size_t size) 323 | { 324 | struct ufat *uf = f->uf; 325 | const struct ufat_bpb *bpb = &uf->bpb; 326 | const unsigned int log2_block_size = uf->dev->log2_block_size; 327 | const unsigned int blocks_per_cluster = 328 | 1 << bpb->log2_blocks_per_cluster; 329 | const unsigned int block_offset = 330 | (f->cur_pos >> log2_block_size) & (blocks_per_cluster - 1); 331 | const unsigned int block_remainder = 332 | blocks_per_cluster - block_offset; 333 | ufat_block_t starting_block; 334 | unsigned int requested_blocks = size >> log2_block_size; 335 | int i; 336 | 337 | if (requested_blocks > block_remainder) 338 | requested_blocks = block_remainder; 339 | if (!requested_blocks) 340 | return 0; 341 | 342 | i = ensure_room(f); 343 | if (i < 0) 344 | return i; 345 | 346 | /* We're writing contiguous whole blocks, so we can bypass the 347 | * cache and perform a single large write. 348 | */ 349 | starting_block = cluster_to_block(bpb, f->cur_cluster) + block_offset; 350 | ufat_cache_invalidate(uf, starting_block, requested_blocks); 351 | 352 | i = uf->dev->write(uf->dev, starting_block, requested_blocks, buf); 353 | if (i < 0) 354 | return -UFAT_ERR_IO; 355 | 356 | uf->stat.write++; 357 | uf->stat.write_blocks += requested_blocks; 358 | 359 | i = advance_ptr(f, requested_blocks << log2_block_size); 360 | if (i < 0) 361 | return i; 362 | 363 | return requested_blocks << log2_block_size; 364 | } 365 | 366 | int ufat_file_write(struct ufat_file *f, const void *buf, ufat_size_t len) 367 | { 368 | const ufat_size_t max_write = ~f->cur_pos; 369 | ufat_size_t total; 370 | int i; 371 | 372 | if (len > max_write) 373 | len = max_write; 374 | total = len; 375 | 376 | /* Write a partial block fragment */ 377 | i = write_block_fragment(f, buf, len); 378 | if (i < 0) 379 | return i; 380 | 381 | buf = (const char*)buf + i; 382 | len -= i; 383 | 384 | /* Write complete blocks */ 385 | for (;;) { 386 | i = write_blocks(f, buf, len); 387 | 388 | if (i < 0) 389 | return i; 390 | 391 | if (!i) 392 | break; 393 | 394 | buf = (const char*)buf + i; 395 | len -= i; 396 | } 397 | 398 | /* Write a partial block fragment */ 399 | i = write_block_fragment(f, buf, len); 400 | if (i < 0) 401 | return i; 402 | 403 | if (f->cur_pos > f->file_size) { 404 | i = set_size(f, f->cur_pos); 405 | if (i < 0) 406 | return i; 407 | } 408 | 409 | return total; 410 | } 411 | 412 | int ufat_file_truncate(struct ufat_file *f) 413 | { 414 | const unsigned int 415 | cluster_size = 1 << (f->uf->bpb.log2_blocks_per_cluster + 416 | f->uf->dev->log2_block_size); 417 | int err; 418 | 419 | err = set_size(f, f->cur_pos); 420 | if (err < 0) 421 | return err; 422 | 423 | if (!f->file_size) { 424 | ufat_cluster_t old_start = f->start; 425 | 426 | err = set_start(f, 0); 427 | if (err < 0) 428 | return err; 429 | 430 | if (UFAT_CLUSTER_IS_PTR(old_start)) { 431 | err = ufat_free_chain(f->uf, old_start); 432 | if (err < 0) 433 | return err; 434 | } 435 | 436 | f->cur_cluster = 0; 437 | return 0; 438 | } 439 | 440 | if (f->file_size & (cluster_size - 1)) { 441 | ufat_cluster_t tail; 442 | 443 | err = ufat_read_fat(f->uf, f->cur_cluster, &tail); 444 | if (err < 0) 445 | return err; 446 | 447 | err = ufat_write_fat(f->uf, f->cur_cluster, UFAT_CLUSTER_EOC); 448 | if (err < 0) 449 | return err; 450 | 451 | if (UFAT_CLUSTER_IS_PTR(tail)) 452 | return ufat_free_chain(f->uf, tail); 453 | 454 | return 0; 455 | } 456 | 457 | if (UFAT_CLUSTER_IS_PTR(f->cur_cluster)) { 458 | err = ufat_write_fat(f->uf, f->prev_cluster, UFAT_CLUSTER_EOC); 459 | if (err < 0) 460 | return err; 461 | 462 | ufat_free_chain(f->uf, f->cur_cluster); 463 | f->cur_cluster = 0; 464 | return 0; 465 | } 466 | 467 | return 0; 468 | } 469 | -------------------------------------------------------------------------------- /ufat_dir.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include "ufat.h" 37 | #include "ufat_internal.h" 38 | 39 | void ufat_open_root(struct ufat *uf, struct ufat_directory *dir) 40 | { 41 | if (uf->bpb.root_cluster) 42 | dir->start = cluster_to_block(&uf->bpb, uf->bpb.root_cluster); 43 | else 44 | dir->start = uf->bpb.root_start; 45 | 46 | dir->uf = uf; 47 | dir->cur_block = dir->start; 48 | dir->cur_pos = 0; 49 | } 50 | 51 | int ufat_open_subdir(struct ufat *uf, struct ufat_directory *dir, 52 | const struct ufat_dirent *ent) 53 | { 54 | if (!(ent->attributes & UFAT_ATTR_DIRECTORY)) 55 | return -UFAT_ERR_NOT_DIRECTORY; 56 | 57 | if (ent->dirent_block == UFAT_BLOCK_NONE || !ent->first_cluster) { 58 | ufat_open_root(uf, dir); 59 | return 0; 60 | } 61 | 62 | dir->uf = uf; 63 | dir->start = cluster_to_block(&uf->bpb, ent->first_cluster); 64 | dir->cur_block = dir->start; 65 | dir->cur_pos = 0; 66 | 67 | return 0; 68 | } 69 | 70 | void ufat_dir_rewind(struct ufat_directory *dir) 71 | { 72 | dir->cur_block = dir->start; 73 | dir->cur_pos = 0; 74 | } 75 | 76 | static int format_name(const struct ufat_lfn_parser *lfn, 77 | const struct ufat_dirent *inf, 78 | char *lfn_buf, int max_len) 79 | { 80 | if (ufat_lfn_ok(lfn)) 81 | return ufat_ucs2_to_utf8(lfn->buf, lfn->len, 82 | lfn_buf, max_len); 83 | 84 | return ufat_format_short(inf->short_name, 85 | inf->short_ext, 86 | lfn_buf, max_len); 87 | } 88 | 89 | int ufat_dir_read(struct ufat_directory *dir, struct ufat_dirent *inf, 90 | char *lfn_buf, int max_len) 91 | { 92 | struct ufat_lfn_parser lfn; 93 | 94 | ufat_lfn_reset(&lfn); 95 | 96 | for (;;) { 97 | uint8_t data[UFAT_DIRENT_SIZE]; 98 | int err; 99 | 100 | inf->dirent_block = dir->cur_block; 101 | inf->dirent_pos = dir->cur_pos; 102 | 103 | err = ufat_read_raw_dirent(dir, data); 104 | if (err) 105 | return err; 106 | 107 | err = ufat_advance_raw_dirent(dir, 0); 108 | if (err) 109 | return err; 110 | 111 | if (data[0x0b] == 0x0f && data[0] != 0xe5) { 112 | ufat_lfn_parse(&lfn, data, 113 | inf->dirent_block, inf->dirent_pos); 114 | } else if (data[0] && data[0] != 0xe5) { 115 | ufat_parse_dirent(dir->uf->bpb.type, data, inf); 116 | 117 | if (ufat_short_checksum(inf->short_name, 118 | inf->short_ext) != 119 | lfn.short_checksum) 120 | ufat_lfn_reset(&lfn); 121 | 122 | if (inf->attributes & 0x08) { 123 | ufat_lfn_reset(&lfn); 124 | continue; 125 | } 126 | 127 | if (ufat_lfn_ok(&lfn)) { 128 | inf->lfn_block = lfn.start_block; 129 | inf->lfn_pos = lfn.start_pos; 130 | } else { 131 | inf->lfn_block = UFAT_BLOCK_NONE; 132 | inf->lfn_pos = 0; 133 | } 134 | 135 | if (lfn_buf) 136 | return format_name(&lfn, inf, lfn_buf, max_len); 137 | 138 | return 0; 139 | } else { 140 | ufat_lfn_reset(&lfn); 141 | } 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | static int verify_empty_dir(struct ufat *uf, struct ufat_dirent *ent) 148 | { 149 | struct ufat_directory dir; 150 | int err; 151 | 152 | err = ufat_open_subdir(uf, &dir, ent); 153 | if (err < 0) 154 | return err; 155 | 156 | for (;;) { 157 | struct ufat_dirent e; 158 | 159 | err = ufat_dir_read(&dir, &e, NULL, 0); 160 | if (err < 0) 161 | return err; 162 | 163 | if (err) 164 | break; 165 | 166 | if (e.short_name[0] != '.') 167 | return -UFAT_ERR_DIRECTORY_NOT_EMPTY; 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | static int delete_entry(struct ufat *uf, struct ufat_dirent *ent) 174 | { 175 | static const uint8_t del_marker = 0xe5; 176 | struct ufat_directory dir; 177 | 178 | dir.uf = uf; 179 | 180 | if (ent->lfn_block == UFAT_BLOCK_NONE) { 181 | dir.cur_block = ent->dirent_block; 182 | dir.cur_pos = ent->dirent_pos; 183 | } else { 184 | dir.cur_block = ent->lfn_block; 185 | dir.cur_pos = ent->lfn_pos; 186 | } 187 | 188 | for (;;) { 189 | int err; 190 | 191 | err = ufat_write_raw_dirent(&dir, &del_marker, 1); 192 | if (err < 0) 193 | return err; 194 | 195 | if (dir.cur_block == ent->dirent_block && 196 | dir.cur_pos == ent->dirent_pos) 197 | break; 198 | 199 | err = ufat_advance_raw_dirent(&dir, 0); 200 | if (err < 0) 201 | return err; 202 | } 203 | 204 | return 0; 205 | } 206 | 207 | int ufat_dir_delete(struct ufat *uf, struct ufat_dirent *ent) 208 | { 209 | int err; 210 | 211 | if (ent->dirent_block == UFAT_BLOCK_NONE || ent->short_name[0] == '.') 212 | return -UFAT_ERR_IMMUTABLE; 213 | 214 | if (ent->attributes & UFAT_ATTR_DIRECTORY) { 215 | err = verify_empty_dir(uf, ent); 216 | if (err < 0) 217 | return err; 218 | } 219 | 220 | err = delete_entry(uf, ent); 221 | if (err < 0) 222 | return err; 223 | 224 | return ufat_free_chain(uf, ent->first_cluster); 225 | } 226 | 227 | /* Create a new empty subdirectory and return it. */ 228 | static int create_empty_dir(struct ufat_directory *parent, 229 | ufat_cluster_t *out, 230 | const struct ufat_dirent *downptr) 231 | { 232 | struct ufat_dirent ent; 233 | ufat_cluster_t c; 234 | int err = ufat_alloc_chain(parent->uf, 1, &c); 235 | int idx; 236 | 237 | if (err < 0) 238 | return err; 239 | 240 | /* Clear all entries */ 241 | err = ufat_init_dirent_cluster(parent->uf, c); 242 | if (err < 0) { 243 | ufat_free_chain(parent->uf, c); 244 | return err; 245 | } 246 | 247 | /* Get the first block of the dirent */ 248 | idx = ufat_cache_open(parent->uf, 249 | cluster_to_block(&parent->uf->bpb, c), 0); 250 | if (idx < 0) { 251 | ufat_free_chain(parent->uf, c); 252 | return idx; 253 | } 254 | 255 | ufat_cache_write(parent->uf, idx); 256 | 257 | /* Create "." */ 258 | memcpy(&ent, downptr, sizeof(ent)); 259 | ent.short_name[0] = '.'; 260 | ent.short_name[1] = 0; 261 | ent.short_ext[0] = 0; 262 | ent.attributes = UFAT_ATTR_DIRECTORY; 263 | ent.first_cluster = c; 264 | ent.file_size = 0; 265 | ufat_pack_dirent(&ent, ufat_cache_data(parent->uf, idx)); 266 | 267 | /* Create ".." */ 268 | ent.short_name[1] = '.'; 269 | ent.short_name[2] = 0; 270 | 271 | if (parent->start >= parent->uf->bpb.cluster_start) 272 | ent.first_cluster = block_to_cluster(&parent->uf->bpb, 273 | parent->start); 274 | else 275 | ent.first_cluster = 0; 276 | 277 | ufat_pack_dirent(&ent, 278 | ufat_cache_data(parent->uf, idx) + UFAT_DIRENT_SIZE); 279 | 280 | *out = c; 281 | return 0; 282 | } 283 | 284 | static int insert_dirent(struct ufat_directory *dir, struct ufat_dirent *ent, 285 | const char *long_name) 286 | { 287 | uint16_t ucs2_name[UFAT_LFN_MAX_CHARS]; 288 | uint8_t data[UFAT_DIRENT_SIZE]; 289 | uint8_t checksum; 290 | int ucs2_len = ufat_utf8_to_ucs2(long_name, ucs2_name); 291 | int num_lfn_frags; 292 | int err; 293 | int i; 294 | 295 | /* Check that the UTF8 was encoded correctly */ 296 | if (ucs2_len < 0) 297 | return ucs2_len; 298 | num_lfn_frags = (ucs2_len + 12) / 13; 299 | 300 | /* Choose a suitable short-name */ 301 | ufat_short_first(long_name, ent->short_name, ent->short_ext); 302 | for (;;) { 303 | err = ufat_short_exists(dir, ent->short_name, ent->short_ext); 304 | if (err < 0) 305 | return err; 306 | 307 | if (!err) 308 | break; 309 | 310 | ufat_short_next(ent->short_name); 311 | } 312 | 313 | checksum = ufat_short_checksum(ent->short_name, ent->short_ext); 314 | 315 | /* Find a space in the directory */ 316 | err = ufat_allocate_raw_dirent(dir, num_lfn_frags + 1); 317 | if (err < 0) 318 | return err; 319 | 320 | ent->lfn_block = dir->cur_block; 321 | ent->lfn_pos = dir->cur_pos; 322 | 323 | /* Write LFN fragments and the DOS dirent */ 324 | for (i = 0; i < num_lfn_frags; i++) { 325 | ufat_lfn_pack_fragment(ucs2_name + (num_lfn_frags - i - 1) * 13, 326 | num_lfn_frags - i, !i, 327 | data, checksum); 328 | 329 | err = ufat_write_raw_dirent(dir, data, sizeof(data)); 330 | if (err < 0) 331 | return err; 332 | 333 | err = ufat_advance_raw_dirent(dir, 0); 334 | if (err < 0) 335 | return err; 336 | } 337 | 338 | ufat_pack_dirent(ent, data); 339 | ent->dirent_block = dir->cur_block; 340 | ent->dirent_pos = dir->cur_pos; 341 | 342 | return ufat_write_raw_dirent(dir, data, sizeof(data)); 343 | } 344 | 345 | int ufat_dir_create(struct ufat_directory *dir, struct ufat_dirent *ent, 346 | const char *name) 347 | { 348 | struct ufat_dirent check; 349 | int err; 350 | 351 | if (!ufat_lfn_is_legal(name)) 352 | return -UFAT_ERR_ILLEGAL_NAME; 353 | 354 | if (!ufat_dir_find(dir, name, &check)) 355 | return -UFAT_ERR_FILE_EXISTS; 356 | 357 | err = create_empty_dir(dir, &ent->first_cluster, ent); 358 | if (err < 0) 359 | return err; 360 | 361 | ent->file_size = 0; 362 | ent->attributes = (ent->attributes & UFAT_ATTR_USER) | 363 | UFAT_ATTR_DIRECTORY; 364 | 365 | err = insert_dirent(dir, ent, name); 366 | if (err < 0) { 367 | ufat_free_chain(dir->uf, ent->first_cluster); 368 | return err; 369 | } 370 | 371 | return 0; 372 | } 373 | 374 | int ufat_dir_mkfile(struct ufat_directory *dir, struct ufat_dirent *ent, 375 | const char *name) 376 | { 377 | struct ufat_dirent check; 378 | int err; 379 | 380 | if (!ufat_lfn_is_legal(name)) 381 | return -UFAT_ERR_ILLEGAL_NAME; 382 | 383 | if (!ufat_dir_find(dir, name, &check)) 384 | return -UFAT_ERR_FILE_EXISTS; 385 | 386 | ent->file_size = 0; 387 | ent->first_cluster = 0; 388 | ent->attributes &= UFAT_ATTR_USER; 389 | 390 | err = insert_dirent(dir, ent, name); 391 | if (err < 0) 392 | return err; 393 | 394 | return 0; 395 | } 396 | 397 | int ufat_dir_find(struct ufat_directory *dir, 398 | const char *target, struct ufat_dirent *inf) 399 | { 400 | ufat_dir_rewind(dir); 401 | 402 | for (;;) { 403 | char name[UFAT_LFN_MAX_UTF8]; 404 | int err = ufat_dir_read(dir, inf, name, sizeof(name)); 405 | 406 | if (err) 407 | return err; 408 | 409 | if (ufat_compare_name(target, name, 0) >= 0) 410 | break; 411 | } 412 | 413 | return 0; 414 | } 415 | 416 | 417 | int ufat_dir_find_path(struct ufat_directory *dir, 418 | const char *path, struct ufat_dirent *ent, 419 | const char **path_out) 420 | { 421 | int at_root = 1; 422 | 423 | ufat_dir_rewind(dir); 424 | 425 | while (*path) { 426 | int len = 0; 427 | 428 | /* Ignore blank components */ 429 | if (*path == '/' || *path == '\\') { 430 | path++; 431 | continue; 432 | } 433 | 434 | /* Descend if necessary */ 435 | if (!at_root) { 436 | int err = ufat_open_subdir(dir->uf, dir, ent); 437 | 438 | if (err < 0) 439 | return err; 440 | } 441 | 442 | /* Search for this component */ 443 | for (;;) { 444 | char name[UFAT_LFN_MAX_UTF8]; 445 | int err = ufat_dir_read(dir, ent, 446 | name, sizeof(name)); 447 | 448 | if (err < 0) 449 | return err; 450 | 451 | if (err) { 452 | if (path_out) 453 | *path_out = path; 454 | return 1; 455 | } 456 | 457 | len = ufat_compare_name(path, name, 1); 458 | if (len >= 0) 459 | break; 460 | } 461 | 462 | /* Skip over this component */ 463 | path += len; 464 | if (*path) 465 | path++; 466 | at_root = 0; 467 | } 468 | 469 | if (at_root) { 470 | /* Pseudo-dirent for root directory */ 471 | memset(ent, 0, sizeof(*ent)); 472 | ent->dirent_block = UFAT_BLOCK_NONE; 473 | ent->attributes = UFAT_ATTR_DIRECTORY; 474 | } 475 | 476 | return 0; 477 | } 478 | 479 | int ufat_get_filename(struct ufat *uf, 480 | const struct ufat_dirent *ent, 481 | char *name_buf, int max_len) 482 | { 483 | struct ufat_lfn_parser lfn; 484 | 485 | ufat_lfn_reset(&lfn); 486 | 487 | if (ent->lfn_block != UFAT_BLOCK_NONE) { 488 | struct ufat_directory dir; 489 | 490 | dir.uf = uf; 491 | dir.cur_block = ent->lfn_block; 492 | dir.cur_pos = ent->lfn_pos; 493 | 494 | while (!(dir.cur_block == ent->dirent_block && 495 | dir.cur_pos == ent->dirent_pos) && 496 | dir.cur_block != UFAT_BLOCK_NONE) { 497 | uint8_t data[UFAT_DIRENT_SIZE]; 498 | int err; 499 | 500 | err = ufat_read_raw_dirent(&dir, data); 501 | if (err < 0) 502 | return err; 503 | 504 | err = ufat_advance_raw_dirent(&dir, 0); 505 | if (err < 0) 506 | return err; 507 | 508 | ufat_lfn_parse(&lfn, data, 509 | dir.cur_block, dir.cur_pos); 510 | } 511 | } 512 | 513 | return format_name(&lfn, ent, name_buf, max_len); 514 | } 515 | 516 | int ufat_move(struct ufat_dirent *ent, struct ufat_directory *dst, 517 | const char *new_name) 518 | { 519 | struct ufat_dirent new_ent; 520 | int err; 521 | 522 | if (!ufat_lfn_is_legal(new_name)) 523 | return -UFAT_ERR_ILLEGAL_NAME; 524 | 525 | if (!ufat_dir_find(dst, new_name, &new_ent)) 526 | return -UFAT_ERR_FILE_EXISTS; 527 | 528 | if (ent->short_name[0] == '.' || ent->dirent_block == UFAT_BLOCK_NONE) 529 | return -UFAT_ERR_IMMUTABLE; 530 | 531 | memcpy(&new_ent, ent, sizeof(new_ent)); 532 | err = insert_dirent(dst, &new_ent, new_name); 533 | if (err < 0) 534 | return err; 535 | 536 | err = delete_entry(dst->uf, ent); 537 | if (err < 0) { 538 | delete_entry(dst->uf, &new_ent); 539 | return err; 540 | } 541 | 542 | return 0; 543 | } 544 | -------------------------------------------------------------------------------- /ufat_mkfs.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include "ufat.h" 36 | #include "ufat_internal.h" 37 | 38 | #define BACKUP_SECTOR 6 39 | #define FSINFO_SECTOR 1 40 | #define MEDIA_DISK 0xf8 41 | 42 | struct fs_layout { 43 | unsigned int log2_sector_size; 44 | unsigned int log2_bpc; 45 | 46 | ufat_block_t reserved_blocks; 47 | ufat_block_t root_blocks; 48 | ufat_block_t fat_blocks; 49 | ufat_block_t logical_blocks; 50 | 51 | ufat_cluster_t clusters; 52 | ufat_fat_type_t type; 53 | }; 54 | 55 | static inline ufat_block_t bytes_to_blocks(unsigned int log2_block_size, 56 | uint32_t bytes) 57 | { 58 | return (bytes + (1 << log2_block_size) - 1) >> log2_block_size; 59 | } 60 | 61 | static int calculate_layout(struct fs_layout *fl, 62 | ufat_block_t nblk, 63 | unsigned int log2_block_size) 64 | { 65 | unsigned int log2_bps; 66 | unsigned int log2_spc; 67 | uint32_t nsect; 68 | ufat_cluster_t clusters_threshold; 69 | ufat_cluster_t est_clusters; 70 | uint32_t fat_bytes; 71 | 72 | /* Make sure the block size is less than or equal to maximum sector 73 | * size (4 kB, log2(4096) = 12). 74 | */ 75 | if (log2_block_size > 12) 76 | return -UFAT_ERR_BLOCK_SIZE; 77 | 78 | /* Minimum sector size is 512 B (log2(512) = 9), but it cannot be 79 | * smaller than block size. 80 | */ 81 | log2_bps = log2_block_size < 9 ? 9 - log2_block_size : 0; 82 | 83 | /* Increase sector size if we can't store the total logical sector count 84 | * in a 32-bit variable. 85 | */ 86 | while (log2_block_size + log2_bps < 12 && 87 | nblk >> log2_bps > UINT32_MAX) 88 | ++log2_bps; 89 | 90 | /* If we still can't fit it, we'll have to chop the device. */ 91 | if (nblk >> log2_bps > UINT32_MAX) 92 | nblk = (ufat_block_t)UINT32_MAX << log2_bps; 93 | 94 | fl->log2_sector_size = log2_block_size + log2_bps; 95 | 96 | /* Calculate total logical sector count. */ 97 | nsect = nblk >> log2_bps; 98 | 99 | /* Threshold values taken from "fatgen103.pdf" - 100 | * https://staff.washington.edu/dittrich/misc/fatgen103.pdf - "FAT 101 | * Volume Initialization" chapter. 102 | * 103 | * For a device with typical 512 B block size this selects FAT12 for 104 | * device size less than ~4.1 MB, FAT16 for device size less than 512 MB 105 | * and FAT32 otherwise. 106 | */ 107 | if (nsect < 8400) { 108 | fl->type = UFAT_TYPE_FAT12; 109 | clusters_threshold = 1 << 12; 110 | log2_spc = 1; 111 | } else if (nsect < 1048576) { 112 | fl->type = UFAT_TYPE_FAT16; 113 | clusters_threshold = 1 << 16; 114 | log2_spc = 1; 115 | } else { 116 | fl->type = UFAT_TYPE_FAT32; 117 | clusters_threshold = 2097152; 118 | log2_spc = 3; 119 | } 120 | 121 | /* Increase cluster size if the resulting number of clusters would be 122 | * above the threshold, but keep it below 32 kB (log2(32768) = 15). 123 | */ 124 | while (log2_spc < 7 && 125 | fl->log2_sector_size + log2_spc < 15 && 126 | nsect >> log2_spc > clusters_threshold) 127 | ++log2_spc; 128 | 129 | fl->log2_bpc = log2_bps + log2_spc; 130 | 131 | /* Calculate the number of reserved blocks. 132 | * 133 | * "fatgen103.pdf" - 134 | * https://staff.washington.edu/dittrich/misc/fatgen103.pdf - "Boot 135 | * Sector and BPB" chapter. 136 | * 137 | * FAT12 and FAT16 should have 1 reserved sector. Typical number of 138 | * reserved sectors for FAT32 is 32. 139 | */ 140 | const ufat_block_t reserved_sectors = 141 | fl->type == UFAT_TYPE_FAT32 ? 32 : 1; 142 | fl->reserved_blocks = reserved_sectors << log2_bps; 143 | 144 | /* Estimate an upper bound on the cluster count and allocate blocks 145 | * for the FAT. 146 | */ 147 | est_clusters = ((nblk - fl->reserved_blocks) >> fl->log2_bpc) + 2; 148 | 149 | if (fl->type == UFAT_TYPE_FAT32) 150 | fat_bytes = est_clusters << 2; 151 | else if (fl->type == UFAT_TYPE_FAT16) 152 | fat_bytes = est_clusters << 1; 153 | else 154 | fat_bytes = (est_clusters * 3 + 1) >> 1; 155 | 156 | fl->fat_blocks = bytes_to_blocks(log2_block_size, fat_bytes); 157 | 158 | /* Calculate the minimum size of the root directory. */ 159 | fl->root_blocks = fl->type != UFAT_TYPE_FAT32 ? 160 | bytes_to_blocks(log2_block_size, 16384) : 0; 161 | 162 | /* Finalize the actual cluster count - it can't be greater than the 163 | * estimate. 164 | */ 165 | fl->clusters = ((nblk - fl->reserved_blocks - 166 | fl->root_blocks - fl->fat_blocks * 2) >> 167 | fl->log2_bpc) + 2; 168 | 169 | /* Expand root directory to fill unusable data space for FAT12/FAT16. */ 170 | if (fl->type != UFAT_TYPE_FAT32) 171 | fl->root_blocks = 172 | nblk - fl->reserved_blocks - fl->fat_blocks * 2 - 173 | ((fl->clusters - 2) << fl->log2_bpc); 174 | 175 | /* Set the block count to exactly fit the filesystem. */ 176 | fl->logical_blocks = ((fl->clusters - 2) << fl->log2_bpc) + 177 | fl->fat_blocks * 2 + fl->reserved_blocks + fl->root_blocks; 178 | 179 | return 0; 180 | } 181 | 182 | static int erase_blocks(struct ufat_device *dev, ufat_block_t start, 183 | ufat_block_t count) 184 | { 185 | const unsigned int block_size = 1 << dev->log2_block_size; 186 | uint8_t buf[block_size]; 187 | 188 | memset(buf, 0, sizeof(buf)); 189 | for (ufat_block_t i = 0; i < count; i++) 190 | if (dev->write(dev, start + i, 1, buf) < 0) 191 | return -UFAT_ERR_IO; 192 | 193 | return 0; 194 | } 195 | 196 | static int erase_reserved_blocks(struct ufat_device *dev, 197 | const struct fs_layout *fl) 198 | { 199 | return erase_blocks(dev, 0, fl->reserved_blocks); 200 | } 201 | 202 | static int write_bpb(struct ufat_device *dev, const struct fs_layout *fl) 203 | { 204 | static const uint8_t boot_header[11] = { 205 | 0xeb, 0xfe, /* jmp $ */ 206 | 0x90, /* nop */ 207 | 'u', 'f', 'a', 't', ' ', ' ', ' ', ' ' 208 | }; 209 | const char *type_name = "FAT "; 210 | const unsigned int log2_bps = 211 | fl->log2_sector_size - dev->log2_block_size; 212 | const unsigned int block_size = 1 << dev->log2_block_size; 213 | uint8_t buf[block_size]; 214 | 215 | switch (fl->type) { 216 | case UFAT_TYPE_FAT12: 217 | type_name = "FAT12 "; 218 | break; 219 | 220 | case UFAT_TYPE_FAT16: 221 | type_name = "FAT16 "; 222 | break; 223 | 224 | case UFAT_TYPE_FAT32: 225 | type_name = "FAT32 "; 226 | break; 227 | } 228 | 229 | memset(buf, 0, sizeof(buf)); 230 | 231 | /* Boot sector signature */ 232 | memcpy(buf, boot_header, sizeof(boot_header)); 233 | buf[0x1fe] = 0x55; 234 | buf[0x1ff] = 0xaa; 235 | 236 | /* BIOS Parameter Block */ 237 | w16(buf + 0x00b, 1 << fl->log2_sector_size); 238 | buf[0x00d] = 1 << (fl->log2_bpc - log2_bps); 239 | w16(buf + 0x00e, fl->reserved_blocks << log2_bps); 240 | buf[0x010] = 2; /* 2 FATs */ 241 | w16(buf + 0x011, fl->root_blocks << (dev->log2_block_size - 5)); 242 | if (fl->type != UFAT_TYPE_FAT32 && fl->logical_blocks <= UINT16_MAX) 243 | w16(buf + 0x013, fl->logical_blocks << log2_bps); 244 | else 245 | w32(buf + 0x020, fl->logical_blocks << log2_bps); 246 | buf[0x015] = MEDIA_DISK; 247 | 248 | if (fl->type != UFAT_TYPE_FAT32) { 249 | w16(buf + 0x016, fl->fat_blocks << log2_bps); 250 | buf[0x026] = 0x29; /* Extended boot signature */ 251 | memset(buf + 0x02b, ' ', 11); /* Volume label */ 252 | memcpy(buf + 0x036, type_name, 8); 253 | } else { 254 | w32(buf + 0x024, fl->fat_blocks << log2_bps); 255 | w32(buf + 0x02c, 2); /* Root directory cluster */ 256 | w16(buf + 0x030, 1); /* FS informations sector */ 257 | w16(buf + 0x032, BACKUP_SECTOR); 258 | buf[0x042] = 0x29; /* Extended boot signature */ 259 | memset(buf + 0x047, ' ', 11); /* Volume label */ 260 | memcpy(buf + 0x052, type_name, 8); 261 | } 262 | 263 | /* Write boot sector */ 264 | if (dev->write(dev, 0, 1, buf) < 0) 265 | return -UFAT_ERR_IO; 266 | 267 | /* Write backup of boot sector in case of FAT32 */ 268 | if (fl->type == UFAT_TYPE_FAT32 && 269 | dev->write(dev, BACKUP_SECTOR >> log2_bps, 1, buf) < 0) 270 | return -UFAT_ERR_IO; 271 | 272 | return 0; 273 | } 274 | 275 | static int write_fsinfo(struct ufat_device *dev, const struct fs_layout *fl) 276 | { 277 | const unsigned int log2_bps = 278 | fl->log2_sector_size - dev->log2_block_size; 279 | const unsigned int block_size = 1 << dev->log2_block_size; 280 | uint8_t buf[block_size]; 281 | 282 | memset(buf, 0, sizeof(buf)); 283 | w32(buf + 0x000, 0x41615252); /* FSI_LeadSig */ 284 | w32(buf + 0x1e4, 0x61417272); /* FSI_StrucSig */ 285 | w32(buf + 0x1e8, fl->clusters - 3); /* FSI_Free_Count */ 286 | w32(buf + 0x1ec, 2); /* FSI_Nxt_Free */ 287 | w32(buf + 0x1fc, 0xaa550000); /* FSI_TrailSig */ 288 | 289 | /* Write FSInfo and its backup */ 290 | const ufat_block_t fsinfo_block = FSINFO_SECTOR >> log2_bps; 291 | const ufat_block_t fsinfo_backup_block = 292 | (FSINFO_SECTOR + BACKUP_SECTOR) >> log2_bps; 293 | if (dev->write(dev, fsinfo_block, 1, buf) < 0 || 294 | dev->write(dev, fsinfo_backup_block, 1, buf) < 0) 295 | return -UFAT_ERR_IO; 296 | 297 | return 0; 298 | } 299 | 300 | static int init_fat12(struct ufat_device *dev, const struct fs_layout *fl) 301 | { 302 | const unsigned int block_size = 1 << dev->log2_block_size; 303 | unsigned int minor_byte = 0; 304 | unsigned int cluster_pair = 0; 305 | ufat_block_t i; 306 | ufat_block_t block; 307 | 308 | for (i = 0, block = 0; i < fl->fat_blocks * 2; i++, block++) { 309 | uint8_t buf[block_size]; 310 | unsigned int j; 311 | 312 | if (block == fl->fat_blocks) { 313 | block = 0; 314 | minor_byte = 0; 315 | cluster_pair = 0; 316 | } 317 | 318 | memset(buf, 0, block_size); 319 | 320 | for (j = 0; j < block_size; j++) { 321 | uint32_t pair_data = 0; 322 | 323 | if ((cluster_pair << 1) >= fl->clusters) 324 | pair_data = 0xff7ff7; 325 | else if ((cluster_pair << 1) + 1 >= fl->clusters) 326 | pair_data = 0xff7000; 327 | 328 | buf[j] = pair_data >> (minor_byte << 3); 329 | 330 | if (++minor_byte >= 3) { 331 | minor_byte = 0; 332 | cluster_pair++; 333 | } 334 | } 335 | 336 | if (block == 0) { 337 | buf[0] = MEDIA_DISK; 338 | buf[1] = 0x8f; 339 | buf[2] = 0xff; 340 | } 341 | 342 | if (dev->write(dev, fl->reserved_blocks + i, 1, buf) < 0) 343 | return -UFAT_ERR_IO; 344 | } 345 | 346 | return 0; 347 | } 348 | 349 | static int init_fat16(struct ufat_device *dev, const struct fs_layout *fl) 350 | { 351 | const unsigned int block_size = 1 << dev->log2_block_size; 352 | ufat_block_t i; 353 | ufat_block_t block; 354 | ufat_cluster_t c; 355 | 356 | for (i = 0, block = 0, c = 0; i < fl->fat_blocks * 2; i++, block++) { 357 | uint8_t buf[block_size]; 358 | unsigned int j; 359 | 360 | if (block == fl->fat_blocks) { 361 | block = 0; 362 | c = 0; 363 | } 364 | 365 | memset(buf, 0, block_size); 366 | 367 | for (j = 0; j < block_size; j += 2) { 368 | if (c >= fl->clusters) 369 | w16(buf + j, 0xfff7); 370 | 371 | c++; 372 | } 373 | 374 | if (block == 0) { 375 | w16(buf, 0xff00 | MEDIA_DISK); 376 | w16(buf + 2, 0xfff8); 377 | } 378 | 379 | if (dev->write(dev, fl->reserved_blocks + i, 1, buf) < 0) 380 | return -UFAT_ERR_IO; 381 | } 382 | 383 | return 0; 384 | } 385 | 386 | static int init_fat32(struct ufat_device *dev, const struct fs_layout *fl) 387 | { 388 | const unsigned int block_size = 1 << dev->log2_block_size; 389 | ufat_block_t i; 390 | ufat_block_t block; 391 | ufat_cluster_t c; 392 | 393 | for (i = 0, block = 0, c = 0; i < fl->fat_blocks * 2; i++, block++) { 394 | uint8_t buf[block_size]; 395 | unsigned int j; 396 | 397 | if (block == fl->fat_blocks) { 398 | block = 0; 399 | c = 0; 400 | } 401 | 402 | memset(buf, 0, block_size); 403 | 404 | for (j = 0; j < block_size; j += 4) { 405 | if (c >= fl->clusters) 406 | w32(buf + j, 0xfffffff7); 407 | 408 | c++; 409 | } 410 | 411 | if (block == 0) { 412 | w32(buf + 0, 0xffffff00 | MEDIA_DISK); 413 | w32(buf + 4, 0xfffffff8); 414 | w32(buf + 8, 0xfffffff8); 415 | } 416 | 417 | if (dev->write(dev, fl->reserved_blocks + i, 1, buf) < 0) 418 | return -UFAT_ERR_IO; 419 | } 420 | 421 | return 0; 422 | } 423 | 424 | static int init_root_blocks(struct ufat_device *dev, const struct fs_layout *fl) 425 | { 426 | const ufat_block_t root_start = 427 | fl->fat_blocks * 2 + fl->reserved_blocks; 428 | 429 | return erase_blocks(dev, root_start, fl->root_blocks); 430 | } 431 | 432 | static int init_root_cluster(struct ufat_device *dev, 433 | const struct fs_layout *fl) 434 | { 435 | const ufat_block_t cluster_start = 436 | fl->fat_blocks * 2 + fl->reserved_blocks + fl->root_blocks; 437 | const unsigned int cluster_blocks = 1 << fl->log2_bpc; 438 | 439 | return erase_blocks(dev, cluster_start, cluster_blocks); 440 | } 441 | 442 | int ufat_mkfs(struct ufat_device *dev, ufat_block_t nblk) 443 | { 444 | struct fs_layout fl; 445 | int err; 446 | 447 | err = calculate_layout(&fl, nblk, dev->log2_block_size); 448 | if (err < 0) 449 | return err; 450 | 451 | err = erase_reserved_blocks(dev, &fl); 452 | if (err < 0) 453 | return err; 454 | 455 | switch (fl.type) { 456 | case UFAT_TYPE_FAT12: 457 | err = init_fat12(dev, &fl); 458 | break; 459 | 460 | case UFAT_TYPE_FAT16: 461 | err = init_fat16(dev, &fl); 462 | break; 463 | 464 | case UFAT_TYPE_FAT32: 465 | err = init_fat32(dev, &fl); 466 | break; 467 | } 468 | 469 | if (err < 0) 470 | return err; 471 | 472 | if (fl.type == UFAT_TYPE_FAT32) 473 | err = init_root_cluster(dev, &fl); 474 | else 475 | err = init_root_blocks(dev, &fl); 476 | 477 | if (err < 0) 478 | return err; 479 | 480 | if (fl.type == UFAT_TYPE_FAT32) { 481 | err = write_fsinfo(dev, &fl); 482 | if (err < 0) 483 | return err; 484 | } 485 | 486 | return write_bpb(dev, &fl); 487 | } 488 | -------------------------------------------------------------------------------- /ufat_ent.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include "ufat.h" 38 | #include "ufat_internal.h" 39 | 40 | int ufat_write_raw_dirent(struct ufat_directory *dir, 41 | const uint8_t *data, unsigned int len) 42 | { 43 | int idx; 44 | 45 | if (dir->cur_block == UFAT_BLOCK_NONE) 46 | return -UFAT_ERR_IO; 47 | 48 | idx = ufat_cache_open(dir->uf, dir->cur_block, 0); 49 | if (idx < 0) 50 | return idx; 51 | 52 | ufat_cache_write(dir->uf, idx); 53 | memcpy(ufat_cache_data(dir->uf, idx) + 54 | dir->cur_pos * UFAT_DIRENT_SIZE, 55 | data, len); 56 | 57 | return 0; 58 | } 59 | 60 | static int advance_block_in_chain(struct ufat_directory *dir, int can_alloc) 61 | { 62 | const struct ufat_bpb *bpb = &dir->uf->bpb; 63 | const ufat_cluster_t cur_cluster = 64 | block_to_cluster(bpb, dir->cur_block); 65 | ufat_block_t next_block = dir->cur_block + 1; 66 | ufat_cluster_t next_cluster = block_to_cluster(bpb, next_block); 67 | int err; 68 | 69 | if (cur_cluster == next_cluster) { 70 | dir->cur_block = next_block; 71 | return 0; 72 | } 73 | 74 | /* We've crossed a cluster boundary. Look up the next cluster in 75 | * the FAT. 76 | */ 77 | err = ufat_read_fat(dir->uf, cur_cluster, &next_cluster); 78 | if (err < 0) 79 | return err; 80 | 81 | if (UFAT_CLUSTER_IS_PTR(next_cluster)) { 82 | dir->cur_block = cluster_to_block(bpb, next_cluster); 83 | return 0; 84 | } 85 | 86 | /* This is the end of the chain. If we can't allocate, we're done. */ 87 | if (!can_alloc) { 88 | dir->cur_block = UFAT_BLOCK_NONE; 89 | return 0; 90 | } 91 | 92 | /* Try to get a new cluster */ 93 | err = ufat_alloc_chain(dir->uf, 1, &next_cluster); 94 | if (err < 0) 95 | return err; 96 | 97 | err = ufat_init_dirent_cluster(dir->uf, next_cluster); 98 | if (err < 0) { 99 | ufat_free_chain(dir->uf, next_cluster); 100 | return err; 101 | } 102 | 103 | err = ufat_write_fat(dir->uf, cur_cluster, next_cluster); 104 | if (err < 0) { 105 | ufat_free_chain(dir->uf, next_cluster); 106 | return err; 107 | } 108 | 109 | dir->cur_block = cluster_to_block(bpb, next_cluster); 110 | return 0; 111 | } 112 | 113 | int ufat_advance_raw_dirent(struct ufat_directory *dir, int can_alloc) 114 | { 115 | /* Advance the dirent pointer and check for a block overrun */ 116 | dir->cur_pos++; 117 | if (dir->cur_pos * UFAT_DIRENT_SIZE >= 118 | (1u << dir->uf->dev->log2_block_size)) { 119 | dir->cur_pos = 0; 120 | 121 | if (dir->cur_block < dir->uf->bpb.cluster_start) { 122 | /* FAT12/16 root directory */ 123 | dir->cur_block++; 124 | if (dir->cur_block >= dir->uf->bpb.cluster_start) 125 | dir->cur_block = UFAT_BLOCK_NONE; 126 | 127 | return 0; 128 | } 129 | 130 | return advance_block_in_chain(dir, can_alloc); 131 | } 132 | 133 | return 0; 134 | } 135 | 136 | int ufat_read_raw_dirent(struct ufat_directory *dir, uint8_t *data) 137 | { 138 | int idx; 139 | 140 | if (dir->cur_block == UFAT_BLOCK_NONE) 141 | return 1; 142 | 143 | idx = ufat_cache_open(dir->uf, dir->cur_block, 0); 144 | if (idx < 0) 145 | return idx; 146 | 147 | memcpy(data, ufat_cache_data(dir->uf, idx) + 148 | dir->cur_pos * UFAT_DIRENT_SIZE, 149 | UFAT_DIRENT_SIZE); 150 | 151 | return 0; 152 | } 153 | 154 | int ufat_allocate_raw_dirent(struct ufat_directory *dir, unsigned int count) 155 | { 156 | ufat_block_t empty_start = UFAT_BLOCK_NONE; 157 | unsigned int empty_pos = 0; 158 | unsigned int empty_count = 0; 159 | 160 | ufat_dir_rewind(dir); 161 | 162 | for (;;) { 163 | uint8_t data[UFAT_DIRENT_SIZE]; 164 | int err; 165 | 166 | err = ufat_read_raw_dirent(dir, data); 167 | if (err < 0) 168 | return err; 169 | 170 | /* Check to see if this raw dirent is empty. Keep track 171 | * of contiguous chains of empty dirents. 172 | */ 173 | if (!data[0] || data[0] == 0xe5) { 174 | if (!empty_count) { 175 | empty_start = dir->cur_block; 176 | empty_pos = dir->cur_pos; 177 | } 178 | 179 | empty_count++; 180 | } else { 181 | empty_count = 0; 182 | } 183 | 184 | /* Is this chain long enough? */ 185 | if (empty_count >= count) 186 | break; 187 | 188 | err = ufat_advance_raw_dirent(dir, 1); 189 | if (err < 0) 190 | return err; 191 | 192 | if (dir->cur_block == UFAT_BLOCK_NONE) 193 | return -UFAT_ERR_DIRECTORY_FULL; 194 | } 195 | 196 | /* Reposition at the start of the empty chain */ 197 | dir->cur_block = empty_start; 198 | dir->cur_pos = empty_pos; 199 | 200 | return 0; 201 | } 202 | 203 | int ufat_init_dirent_cluster(struct ufat *uf, ufat_cluster_t c) 204 | { 205 | const struct ufat_bpb *bpb = &uf->bpb; 206 | const ufat_block_t start = cluster_to_block(bpb, c); 207 | const unsigned int count = 208 | 1 << bpb->log2_blocks_per_cluster; 209 | const unsigned int block_size = 1 << uf->dev->log2_block_size; 210 | int i; 211 | 212 | for (i = count - 1; i >= 0; i--) { 213 | int idx = ufat_cache_open(uf, start + i, 1); 214 | 215 | if (idx < 0) 216 | return idx; 217 | 218 | ufat_cache_write(uf, idx); 219 | memset(ufat_cache_data(uf, idx), 0, block_size); 220 | } 221 | 222 | return 0; 223 | } 224 | 225 | static void sn_copy(const uint8_t *src, char *dst, int len) 226 | { 227 | int i; 228 | 229 | for (i = 0; i < len && src[i] > ' '; i++) 230 | dst[i] = src[i]; 231 | 232 | dst[i] = 0; 233 | } 234 | 235 | void ufat_parse_dirent(ufat_fat_type_t type, 236 | const uint8_t *data, struct ufat_dirent *inf) 237 | { 238 | sn_copy(data, inf->short_name, 8); 239 | sn_copy(data + 0x08, inf->short_ext, 3); 240 | 241 | inf->attributes = data[0x0b]; 242 | inf->create_time = r16(data + 0x0e); 243 | inf->create_date = r16(data + 0x10); 244 | inf->access_date = r16(data + 0x12); 245 | inf->modify_time = r16(data + 0x16); 246 | inf->modify_date = r16(data + 0x18); 247 | inf->file_size = r32(data + 0x1c); 248 | inf->first_cluster = r16(data + 0x1a); 249 | 250 | if (type == UFAT_TYPE_FAT32) { 251 | uint32_t high = r16(data + 0x14); 252 | 253 | inf->first_cluster |= high << 16; 254 | } 255 | } 256 | 257 | void ufat_pack_dirent(const struct ufat_dirent *ent, uint8_t *data) 258 | { 259 | memset(data, 0x20, 11); 260 | memcpy(data, ent->short_name, strlen(ent->short_name)); 261 | memcpy(data + 0x08, ent->short_ext, strlen(ent->short_ext)); 262 | 263 | data[0x0b] = ent->attributes; 264 | data[0x0c] = 0; 265 | data[0x0d] = 0; 266 | w16(data + 0x0e, ent->create_time); 267 | w16(data + 0x10, ent->create_date); 268 | w16(data + 0x12, ent->access_date); 269 | w16(data + 0x14, ent->first_cluster >> 16); 270 | w16(data + 0x16, ent->modify_time); 271 | w16(data + 0x18, ent->modify_date); 272 | w16(data + 0x1a, ent->first_cluster & 0xffff); 273 | w32(data + 0x1c, ent->file_size); 274 | } 275 | 276 | /************************************************************************ 277 | * Name/LFN manipulation 278 | */ 279 | 280 | int ufat_lfn_is_legal(const char *name) 281 | { 282 | if (!*name) 283 | return 0; 284 | 285 | if (name[0] == '.') { 286 | if (!name[1]) 287 | return 0; 288 | if (name[1] == '.' && !name[2]) 289 | return 0; 290 | } 291 | 292 | while (*name) { 293 | if (*name == '\\' || *name == '/' || *name < ' ') 294 | return 0; 295 | 296 | name++; 297 | } 298 | 299 | return 1; 300 | } 301 | 302 | static int is_legal_dos_char(char c) 303 | { 304 | return isalnum(c) || 305 | (c >= '!' && c <= ')') || 306 | (c == '-') || 307 | (c == '@') || 308 | (c >= '^' && c <= '`') || 309 | (c == '{' || c == '}' || c == '~'); 310 | } 311 | 312 | void ufat_short_first(const char *long_name, 313 | char *short_name, char *ext_text) 314 | { 315 | int len = strlen(long_name); 316 | int ext = len; 317 | int i, j; 318 | 319 | while (ext >= 0 && long_name[ext] != '.') 320 | ext--; 321 | 322 | if (ext > 0) 323 | ext++; 324 | else 325 | ext = len; 326 | 327 | j = 0; 328 | for (i = ext; j < 3 && long_name[i]; i++) { 329 | char c = long_name[i]; 330 | 331 | if (is_legal_dos_char(c)) 332 | ext_text[j++] = toupper(c); 333 | } 334 | ext_text[j] = 0; 335 | 336 | j = 0; 337 | for (i = 0; j < 8 && i < ext; i++) { 338 | char c = long_name[i]; 339 | 340 | if (is_legal_dos_char(c)) 341 | short_name[j++] = toupper(c); 342 | } 343 | short_name[j] = 0; 344 | 345 | if (!j) { 346 | for (i = 0; i < 8; i++) 347 | short_name[i] = '~'; 348 | short_name[8] = 0; 349 | } 350 | } 351 | 352 | void ufat_short_next(char *short_name) 353 | { 354 | int len = strlen(short_name); 355 | int tilde = strlen(short_name); 356 | unsigned int suffix_num; 357 | int i; 358 | 359 | while (tilde >= 0 && short_name[tilde] != '~') 360 | tilde--; 361 | if (tilde < 0) 362 | tilde = len; 363 | else 364 | tilde++; 365 | 366 | suffix_num = atoi(short_name + tilde) + 1; 367 | short_name[8] = 0; 368 | 369 | i = 7; 370 | while (suffix_num && i >= 0) { 371 | short_name[i] = (suffix_num % 10) + '0'; 372 | suffix_num /= 10; 373 | i--; 374 | } 375 | 376 | if (i >= 0) 377 | short_name[i--] = '~'; 378 | 379 | while (i >= tilde) 380 | short_name[i--] = '~'; 381 | } 382 | 383 | int ufat_short_exists(struct ufat_directory *dir, const char *short_name, 384 | const char *short_ext) 385 | { 386 | struct ufat_dirent ent; 387 | 388 | ufat_dir_rewind(dir); 389 | for (;;) { 390 | int err = ufat_dir_read(dir, &ent, NULL, 0); 391 | 392 | if (err < 0) 393 | return err; 394 | 395 | if (err) 396 | break; 397 | 398 | if (ufat_compare_name(short_name, ent.short_name, 0) >= 0 && 399 | ufat_compare_name(short_ext, ent.short_ext, 0) >= 0) 400 | return 1; 401 | } 402 | 403 | return 0; 404 | } 405 | 406 | void ufat_lfn_parse(struct ufat_lfn_parser *s, const uint8_t *data, 407 | ufat_block_t blk, unsigned int pos) 408 | { 409 | const int fr_seq = data[0]; 410 | uint16_t frag_data[13]; 411 | int fr_pos; 412 | int fr_len; 413 | int i; 414 | 415 | fr_pos = ((fr_seq & 0x3f) - 1) * 13; 416 | fr_len = 13; 417 | if (fr_pos + fr_len > UFAT_LFN_MAX_CHARS) 418 | fr_len = UFAT_LFN_MAX_CHARS - fr_pos; 419 | 420 | /* Check against expected sequence number and checksum */ 421 | if (fr_seq & 0x40) { 422 | s->start_block = blk; 423 | s->start_pos = pos; 424 | s->seq = fr_seq & 0x3f; 425 | s->len = fr_pos + fr_len; 426 | s->short_checksum = data[0x0d]; 427 | } else if (fr_seq != s->seq || 428 | s->short_checksum != data[0x0d]) { 429 | ufat_lfn_reset(s); 430 | return; 431 | } 432 | s->seq--; 433 | 434 | /* Unpack and copy fragment */ 435 | for (i = 0; i < 5; i++) 436 | frag_data[i] = r16(data + 0x01 + i * 2); 437 | for (i = 0; i < 6; i++) 438 | frag_data[i + 5] = r16(data + 0x0e + i * 2); 439 | frag_data[11] = r16(data + 0x1c); 440 | frag_data[12] = r16(data + 0x1e); 441 | 442 | for (i = 0; i < fr_len; i++) 443 | s->buf[fr_pos + i] = frag_data[i]; 444 | } 445 | 446 | int ufat_ucs2_to_utf8(const uint16_t *src, int src_len, 447 | char *dst, int dst_len) 448 | { 449 | int i; 450 | int j = 0; 451 | 452 | for (i = 0; i < src_len && src[i]; i++) { 453 | uint16_t c = src[i]; 454 | 455 | if (c >= 0x800) { 456 | if (j + 3 >= dst_len) 457 | return -UFAT_ERR_BAD_ENCODING; 458 | 459 | dst[j++] = 0xe0 | (c >> 12); 460 | dst[j++] = 0x80 | ((c >> 6) & 0x3f); 461 | dst[j++] = 0x80 | (c & 0x3f); 462 | } else if (c >= 0x80) { 463 | if (j + 2 >= dst_len) 464 | return -UFAT_ERR_BAD_ENCODING; 465 | 466 | dst[j++] = 0xc0 | c >> 6; 467 | dst[j++] = 0x80 | (c & 0x3f); 468 | } else { 469 | if (j + 1 >= dst_len) 470 | return -UFAT_ERR_BAD_ENCODING; 471 | 472 | dst[j++] = c; 473 | } 474 | } 475 | 476 | if (j >= dst_len) 477 | return -UFAT_ERR_NAME_TOO_LONG; 478 | 479 | dst[j] = 0; 480 | return 0; 481 | } 482 | 483 | static inline uint8_t cksum_next(uint8_t cur, char c) 484 | { 485 | return (((cur & 1) << 7) | (cur >> 1)) + c; 486 | } 487 | 488 | uint8_t ufat_short_checksum(const char *short_name, const char *short_ext) 489 | { 490 | uint8_t sum = 0; 491 | int i; 492 | 493 | for (i = 0; short_name[i]; i++) 494 | sum = cksum_next(sum, short_name[i]); 495 | for (; i < 8; i++) 496 | sum = cksum_next(sum, ' '); 497 | 498 | for (i = 0; short_ext[i]; i++) 499 | sum = cksum_next(sum, short_ext[i]); 500 | for (; i < 3; i++) 501 | sum = cksum_next(sum, ' '); 502 | 503 | return sum; 504 | } 505 | 506 | int ufat_utf8_to_ucs2(const char *src, uint16_t *dst) 507 | { 508 | int len = 0; 509 | int i; 510 | 511 | while (*src && len < UFAT_LFN_MAX_CHARS) { 512 | unsigned int c = *src; 513 | 514 | if ((c & 0xf0) == 0xf0) 515 | return -UFAT_ERR_ILLEGAL_NAME; 516 | 517 | if ((c & 0xe0) == 0xe0) { 518 | unsigned int b = src[1]; 519 | unsigned int a = src[2]; 520 | 521 | if ((a & 0xc0) != 0xc0 || 522 | (b & 0xc0) != 0xc0) 523 | return -UFAT_ERR_BAD_ENCODING; 524 | 525 | dst[len++] = ((c & 0xf) << 12) | 526 | ((b & 0x3f) << 6) | 527 | (a & 0x3f); 528 | src += 3; 529 | } else if ((c & 0xc0) == 0xc0) { 530 | unsigned int b = src[1]; 531 | 532 | if ((b & 0xc0) != 0xc0) 533 | return -UFAT_ERR_BAD_ENCODING; 534 | 535 | dst[len++] = ((c & 0x1f) << 6) | (b & 0x3f); 536 | src += 2; 537 | } else if (c & 0x80) { 538 | return -UFAT_ERR_BAD_ENCODING; 539 | } else { 540 | dst[len++] = c; 541 | src++; 542 | } 543 | } 544 | 545 | if (*src) 546 | return -UFAT_ERR_NAME_TOO_LONG; 547 | 548 | /* Pad out to full length */ 549 | for (i = len; i < UFAT_LFN_MAX_CHARS; i++) 550 | dst[i] = 0; 551 | 552 | return len; 553 | } 554 | 555 | void ufat_lfn_pack_fragment(const uint16_t *ucs, int seq, int is_first, 556 | uint8_t *data, uint8_t checksum) 557 | { 558 | int i; 559 | 560 | data[0] = seq; 561 | if (is_first) 562 | data[0] |= 0x40; 563 | 564 | for (i = 0; i < 5; i++) 565 | w16(data + 0x01 + i * 2, ucs[i]); 566 | 567 | data[0x0b] = UFAT_ATTR_LFN_FRAGMENT; 568 | data[0x0c] = 0; 569 | data[0x0d] = checksum; 570 | 571 | for (i = 0; i < 6; i++) 572 | w16(data + 0x0e + i * 2, ucs[i + 5]); 573 | 574 | data[0x1a] = 0; 575 | data[0x1b] = 0; 576 | 577 | w16(data + 0x1c, ucs[11]); 578 | w16(data + 0x1e, ucs[12]); 579 | } 580 | 581 | int ufat_format_short(const char *name, const char *ext, 582 | char *out, int max_len) 583 | { 584 | int i = 0; 585 | 586 | while (*name) { 587 | if (i >= max_len) 588 | return -UFAT_ERR_NAME_TOO_LONG; 589 | 590 | out[i++] = *(name++); 591 | } 592 | 593 | if (*ext) { 594 | if (i >= max_len) 595 | return -UFAT_ERR_NAME_TOO_LONG; 596 | out[i++] = '.'; 597 | 598 | while (*ext) { 599 | if (i >= max_len) 600 | return -UFAT_ERR_NAME_TOO_LONG; 601 | out[i++] = *(ext++); 602 | } 603 | } 604 | 605 | if (i >= max_len) 606 | return -UFAT_ERR_NAME_TOO_LONG; 607 | 608 | out[i] = 0; 609 | return 0; 610 | } 611 | 612 | int ufat_compare_name(const char *search_name, const char *dir_name, 613 | int component_only) 614 | { 615 | int m = 0; 616 | 617 | while (search_name[m]) { 618 | int dc = dir_name[m]; 619 | int sc = search_name[m]; 620 | 621 | if (component_only && (sc == '/' || sc == '\\')) 622 | break; 623 | 624 | if (!dc) 625 | return -1; 626 | 627 | if (sc >= 'a' && sc <= 'z') 628 | sc -= 32; 629 | 630 | if (dc >= 'a' && dc <= 'z') 631 | dc -= 32; 632 | 633 | if (sc != dc) 634 | return -1; 635 | 636 | m++; 637 | } 638 | 639 | if (dir_name[m]) 640 | return -1; 641 | 642 | return m; 643 | } 644 | 645 | int ufat_update_attributes(struct ufat *uf, struct ufat_dirent *ent) 646 | { 647 | int idx = ufat_cache_open(uf, ent->dirent_block, 0); 648 | uint8_t *data; 649 | struct ufat_dirent old; 650 | 651 | if (idx < 0) 652 | return idx; 653 | 654 | data = ufat_cache_data(uf, idx) + ent->dirent_pos * UFAT_DIRENT_SIZE; 655 | ufat_parse_dirent(uf->bpb.type, data, &old); 656 | 657 | if (!(old.attributes & UFAT_ATTR_DIRECTORY)) 658 | old.attributes = (old.attributes & ~UFAT_ATTR_USER) | 659 | (ent->attributes & UFAT_ATTR_USER); 660 | 661 | old.create_date = ent->create_date; 662 | old.create_time = ent->create_time; 663 | old.modify_date = ent->modify_date; 664 | old.modify_time = ent->modify_time; 665 | old.access_date = ent->access_date; 666 | 667 | ufat_cache_write(uf, idx); 668 | ufat_pack_dirent(&old, data); 669 | 670 | return 0; 671 | } 672 | -------------------------------------------------------------------------------- /ufat.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include "ufat.h" 36 | #include "ufat_internal.h" 37 | 38 | static int cache_flush(struct ufat *uf, unsigned int cache_index) 39 | { 40 | struct ufat_cache_desc *d = &uf->cache_desc[cache_index]; 41 | 42 | if (!(d->flags & UFAT_CACHE_FLAG_DIRTY) || 43 | !(d->flags & UFAT_CACHE_FLAG_PRESENT)) 44 | return 0; 45 | 46 | if (uf->dev->write(uf->dev, d->index, 1, 47 | ufat_cache_data(uf, cache_index)) < 0) 48 | return -UFAT_ERR_IO; 49 | 50 | uf->stat.cache_flush++; 51 | uf->stat.write++; 52 | uf->stat.write_blocks++; 53 | 54 | /* If this block is part of the FAT, mirror it to the other FATs. Not 55 | * a fatal error if this fails. 56 | */ 57 | if (d->index >= uf->bpb.fat_start && 58 | d->index < uf->bpb.fat_start + uf->bpb.fat_size) { 59 | unsigned int i; 60 | ufat_block_t b = d->index; 61 | 62 | for (i = 1; i < uf->bpb.fat_count; i++) { 63 | b += uf->bpb.fat_size; 64 | uf->dev->write(uf->dev, b, 1, 65 | ufat_cache_data(uf, cache_index)); 66 | 67 | uf->stat.write++; 68 | uf->stat.write_blocks++; 69 | } 70 | } 71 | 72 | d->flags &= ~UFAT_CACHE_FLAG_DIRTY; 73 | return 0; 74 | } 75 | 76 | int ufat_cache_evict(struct ufat *uf, ufat_block_t start, ufat_block_t count) 77 | { 78 | unsigned int i; 79 | 80 | for (i = 0; i < uf->cache_size; i++) { 81 | struct ufat_cache_desc *d = &uf->cache_desc[i]; 82 | 83 | if ((d->flags & UFAT_CACHE_FLAG_PRESENT) && 84 | d->index >= start && d->index < start + count) { 85 | int err = cache_flush(uf, i); 86 | 87 | if (err < 0) 88 | return err; 89 | 90 | d->flags = 0; 91 | } 92 | } 93 | 94 | return 0; 95 | } 96 | 97 | void ufat_cache_invalidate(struct ufat *uf, ufat_block_t start, 98 | ufat_block_t count) 99 | { 100 | unsigned int i; 101 | 102 | for (i = 0; i < uf->cache_size; i++) { 103 | struct ufat_cache_desc *d = &uf->cache_desc[i]; 104 | 105 | if ((d->flags & UFAT_CACHE_FLAG_PRESENT) && 106 | d->index >= start && d->index < start + count) 107 | d->flags = 0; 108 | } 109 | } 110 | 111 | int ufat_cache_open(struct ufat *uf, ufat_block_t blk_index, int skip_read) 112 | { 113 | unsigned int i; 114 | int oldest = -1; 115 | int free = -1; 116 | int err; 117 | unsigned int oldest_age = 0; 118 | 119 | /* Scan the cache, looking for: 120 | * 121 | * (a) the item, if we already have it. 122 | * (b) a free slot, if one exists. 123 | * (c) the oldest cache item. 124 | */ 125 | for (i = 0; i < uf->cache_size; i++) { 126 | struct ufat_cache_desc *d = &uf->cache_desc[i]; 127 | unsigned int age = uf->next_seq - d->seq; 128 | 129 | if ((d->flags & UFAT_CACHE_FLAG_PRESENT) && 130 | d->index == blk_index) { 131 | d->seq = uf->next_seq++; 132 | uf->stat.cache_hit++; 133 | return i; 134 | } 135 | 136 | if (!(d->flags & UFAT_CACHE_FLAG_PRESENT)) 137 | free = i; 138 | 139 | if (oldest < 0 || age > oldest_age) { 140 | oldest_age = age; 141 | oldest = i; 142 | } 143 | } 144 | 145 | /* We don't have the item. Find a place to put it. */ 146 | if (free >= 0) { 147 | i = free; 148 | } else { 149 | err = cache_flush(uf, oldest); 150 | if (err < 0) 151 | return err; 152 | 153 | i = oldest; 154 | } 155 | 156 | if (skip_read == 0) { 157 | /* Read it in */ 158 | err = uf->dev->read(uf->dev, blk_index, 1, 159 | ufat_cache_data(uf, i)); 160 | if (err < 0) { 161 | uf->cache_desc[i].flags = 0; 162 | return err; 163 | } 164 | 165 | uf->stat.read++; 166 | uf->stat.read_blocks++; 167 | } else 168 | memset(ufat_cache_data(uf, i), 0, 169 | 1 << uf->dev->log2_block_size); 170 | 171 | struct ufat_cache_desc *d = &uf->cache_desc[i]; 172 | d->flags = UFAT_CACHE_FLAG_PRESENT; 173 | d->index = blk_index; 174 | d->seq = uf->next_seq++; 175 | 176 | uf->stat.cache_miss++; 177 | 178 | return i; 179 | } 180 | 181 | static int log2_exact(unsigned int e, unsigned int *ret) 182 | { 183 | unsigned int count = 0; 184 | 185 | if (!e) 186 | return -1; 187 | 188 | while (e > 1) { 189 | if (e & 1) 190 | return -1; 191 | 192 | e >>= 1; 193 | count++; 194 | } 195 | 196 | *ret = count; 197 | return 0; 198 | } 199 | 200 | static int parse_bpb(unsigned int log2_bytes_per_block, 201 | struct ufat_bpb *ufb, uint8_t *bpb) 202 | { 203 | const uint16_t bytes_per_sector = r16(bpb + 0x00b); 204 | const uint8_t sectors_per_cluster = bpb[0x00d]; 205 | const uint16_t reserved_sector_count = r16(bpb + 0x00e); 206 | const uint16_t root_entries = r16(bpb + 0x011); 207 | uint32_t sectors_per_fat = r16(bpb + 0x016); 208 | uint32_t total_logical_sectors = r16(bpb + 0x013); 209 | const uint8_t number_of_fats = bpb[0x010]; 210 | const uint32_t root_cluster = r32(bpb + 0x02c); 211 | unsigned int log2_bytes_per_sector = 0; 212 | unsigned int log2_sectors_per_cluster = 0; 213 | const unsigned int root_sectors = 214 | (root_entries * UFAT_DIRENT_SIZE + bytes_per_sector - 1) / 215 | bytes_per_sector; 216 | 217 | /* Read and check BPB values */ 218 | if (log2_bytes_per_block < 9) 219 | return -UFAT_ERR_BLOCK_SIZE; 220 | 221 | if (!total_logical_sectors) 222 | total_logical_sectors = r32(bpb + 0x020); 223 | if (!sectors_per_fat) 224 | sectors_per_fat = r32(bpb + 0x024); 225 | 226 | if (log2_exact(bytes_per_sector, &log2_bytes_per_sector) < 0 || 227 | log2_exact(sectors_per_cluster, &log2_sectors_per_cluster) < 0) 228 | return -UFAT_ERR_INVALID_BPB; 229 | 230 | if (r16(bpb + 0x1fe) != 0xaa55) 231 | return -UFAT_ERR_INVALID_BPB; 232 | 233 | /* Convert sectors to blocks */ 234 | if (log2_bytes_per_block > log2_bytes_per_sector) { 235 | const unsigned int shift = 236 | log2_bytes_per_block - log2_bytes_per_sector; 237 | 238 | if (log2_sectors_per_cluster < shift) 239 | return -UFAT_ERR_BLOCK_ALIGNMENT; 240 | ufb->log2_blocks_per_cluster = 241 | log2_sectors_per_cluster - shift; 242 | 243 | if ((reserved_sector_count | sectors_per_fat | root_sectors) & 244 | ((1 << shift) - 1)) 245 | return -UFAT_ERR_BLOCK_ALIGNMENT; 246 | ufb->fat_start = reserved_sector_count >> shift; 247 | ufb->fat_size = sectors_per_fat >> shift; 248 | ufb->root_size = root_sectors >> shift; 249 | } else { 250 | const unsigned int shift = 251 | log2_bytes_per_sector - log2_bytes_per_block; 252 | 253 | ufb->log2_blocks_per_cluster = 254 | log2_sectors_per_cluster + shift; 255 | ufb->fat_start = reserved_sector_count << shift; 256 | ufb->fat_size = sectors_per_fat << shift; 257 | ufb->root_size = root_sectors << shift; 258 | } 259 | 260 | if (!number_of_fats) 261 | return -UFAT_ERR_INVALID_BPB; 262 | 263 | /* Various block-size independent values */ 264 | ufb->fat_count = number_of_fats; 265 | ufb->num_clusters = 266 | ((total_logical_sectors - reserved_sector_count - 267 | sectors_per_fat * number_of_fats - 268 | root_sectors) >> 269 | log2_sectors_per_cluster) + 2; 270 | ufb->root_cluster = root_cluster & UFAT_CLUSTER_MASK; 271 | ufb->root_start = ufb->fat_start + ufb->fat_size * ufb->fat_count; 272 | ufb->cluster_start = ufb->root_start + ufb->root_size; 273 | 274 | /* Figure out filesystem type */ 275 | if (!root_sectors) { 276 | ufb->type = UFAT_TYPE_FAT32; 277 | } else { 278 | ufb->root_cluster = 0; 279 | if (ufb->num_clusters <= UFAT_MAX_FAT12) 280 | ufb->type = UFAT_TYPE_FAT12; 281 | else 282 | ufb->type = UFAT_TYPE_FAT16; 283 | } 284 | 285 | return 0; 286 | } 287 | 288 | static int read_bpb(struct ufat *uf) 289 | { 290 | int idx; 291 | 292 | idx = ufat_cache_open(uf, 0, 0); 293 | if (idx < 0) 294 | return idx; 295 | 296 | return parse_bpb(uf->dev->log2_block_size, &uf->bpb, 297 | ufat_cache_data(uf, idx)); 298 | } 299 | 300 | int ufat_open(struct ufat *uf, const struct ufat_device *dev) 301 | { 302 | uf->dev = dev; 303 | 304 | uf->next_seq = 0; 305 | uf->cache_size = UFAT_CACHE_BYTES >> dev->log2_block_size; 306 | 307 | if (uf->cache_size > UFAT_CACHE_MAX_BLOCKS) 308 | uf->cache_size = UFAT_CACHE_MAX_BLOCKS; 309 | 310 | if (!uf->cache_size) 311 | return -UFAT_ERR_BLOCK_SIZE; 312 | 313 | uf->alloc_ptr = 0; 314 | memset(&uf->stat, 0, sizeof(uf->stat)); 315 | memset(&uf->cache_desc, 0, sizeof(uf->cache_desc)); 316 | 317 | return read_bpb(uf); 318 | } 319 | 320 | int ufat_sync(struct ufat *uf) 321 | { 322 | unsigned int i; 323 | int ret = 0; 324 | 325 | for (i = 0; i < uf->cache_size; i++) { 326 | int err = cache_flush(uf, i); 327 | 328 | if (err) 329 | ret = err; 330 | } 331 | 332 | return ret; 333 | } 334 | 335 | int ufat_count_free_clusters(struct ufat *uf, ufat_cluster_t *free_clusters) 336 | { 337 | ufat_cluster_t idx; 338 | ufat_cluster_t local_free_clusters = 0; 339 | const ufat_cluster_t total = uf->bpb.num_clusters; 340 | 341 | /* Skip first two "special" clusters */ 342 | for (idx = 2; idx < total; idx++) { 343 | ufat_cluster_t c; 344 | int err; 345 | 346 | /* Never use this cluster index in a FAT12 system */ 347 | if (idx == 0xff0 && uf->bpb.type == UFAT_TYPE_FAT12) 348 | continue; 349 | 350 | err = ufat_read_fat(uf, idx, &c); 351 | if (err < 0) 352 | return err; 353 | 354 | if (c == UFAT_CLUSTER_FREE) 355 | local_free_clusters++; 356 | } 357 | 358 | *free_clusters = local_free_clusters; 359 | return 0; 360 | } 361 | 362 | void ufat_close(struct ufat *uf) 363 | { 364 | ufat_sync(uf); 365 | } 366 | 367 | const char *ufat_strerror(int err) 368 | { 369 | static const char *const text[UFAT_MAX_ERR] = { 370 | [UFAT_OK] = "No error", 371 | [UFAT_ERR_IO] = "IO error", 372 | [UFAT_ERR_BLOCK_SIZE] = "Invalid block size", 373 | [UFAT_ERR_INVALID_BPB] = "Invalid BPB", 374 | [UFAT_ERR_BLOCK_ALIGNMENT] = 375 | "Filesystem is not aligned for this block size", 376 | [UFAT_ERR_INVALID_CLUSTER] = "Invalid cluster index", 377 | [UFAT_ERR_NAME_TOO_LONG] = "Filename too long", 378 | [UFAT_ERR_NOT_DIRECTORY] = "Not a directory", 379 | [UFAT_ERR_NOT_FILE] = "Not a file", 380 | [UFAT_ERR_IMMUTABLE] = "Can't delete/modify this entry", 381 | [UFAT_ERR_DIRECTORY_NOT_EMPTY] = "Directory not empty", 382 | [UFAT_ERR_ILLEGAL_NAME] = "Illegal filename", 383 | [UFAT_ERR_FILE_EXISTS] = "File already exists", 384 | [UFAT_ERR_BAD_ENCODING] = "Bad encoding", 385 | [UFAT_ERR_DIRECTORY_FULL] = "Directory is full", 386 | [UFAT_ERR_NO_CLUSTERS] = "No free clusters" 387 | }; 388 | 389 | if (err < 0) 390 | err = -err; 391 | 392 | if (err >= UFAT_MAX_ERR) 393 | return "Invalid error code"; 394 | 395 | return text[err]; 396 | } 397 | 398 | static int read_fat_byte(struct ufat *uf, unsigned int offset, uint8_t *out) 399 | { 400 | ufat_block_t b = offset >> uf->dev->log2_block_size; 401 | unsigned int r = offset & ((1 << uf->dev->log2_block_size) - 1); 402 | int idx; 403 | 404 | idx = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 405 | if (idx < 0) 406 | return idx; 407 | 408 | *out = ufat_cache_data(uf, idx)[r]; 409 | return 0; 410 | } 411 | 412 | static int read_fat12(struct ufat *uf, ufat_cluster_t index, 413 | ufat_cluster_t *out) 414 | { 415 | unsigned int offset = (index >> 1) * 3; 416 | uint8_t a; 417 | uint8_t b; 418 | uint16_t raw; 419 | int err; 420 | 421 | err = read_fat_byte(uf, offset + 1, &a); 422 | if (err < 0) 423 | return err; 424 | 425 | if (index & 1) { 426 | err = read_fat_byte(uf, offset + 2, &b); 427 | if (err < 0) 428 | return err; 429 | 430 | raw = (((ufat_cluster_t)b) << 4) | 431 | (((ufat_cluster_t)a) >> 4); 432 | } else { 433 | err = read_fat_byte(uf, offset, &b); 434 | if (err < 0) 435 | return err; 436 | 437 | raw = ((ufat_cluster_t)b) | 438 | ((((ufat_cluster_t)a) & 0xf) << 8); 439 | } 440 | 441 | if (raw >= 0xff8 || raw == 0xff0) { 442 | *out = UFAT_CLUSTER_EOC; 443 | return 0; 444 | } 445 | 446 | if (raw == 0xff7) { 447 | *out = UFAT_CLUSTER_BAD; 448 | return 0; 449 | } 450 | 451 | *out = raw; 452 | return 0; 453 | } 454 | 455 | static int read_fat16(struct ufat *uf, ufat_cluster_t index, 456 | ufat_cluster_t *out) 457 | { 458 | const unsigned int shift = uf->dev->log2_block_size - 1; 459 | const unsigned int b = index >> shift; 460 | const unsigned int r = index & ((1 << shift) - 1); 461 | int i = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 462 | uint16_t raw; 463 | 464 | if (i < 0) 465 | return i; 466 | 467 | raw = r16(ufat_cache_data(uf, i) + r * 2); 468 | 469 | if (raw >= 0xfff8) { 470 | *out = UFAT_CLUSTER_EOC; 471 | return 0; 472 | } 473 | 474 | if (raw == 0xfff7) { 475 | *out = UFAT_CLUSTER_BAD; 476 | return 0; 477 | } 478 | 479 | *out = raw; 480 | return 0; 481 | } 482 | 483 | static int read_fat32(struct ufat *uf, ufat_cluster_t index, 484 | ufat_cluster_t *out) 485 | { 486 | const unsigned int shift = uf->dev->log2_block_size - 2; 487 | const unsigned int b = index >> shift; 488 | const unsigned int r = index & ((1 << shift) - 1); 489 | int i = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 490 | uint32_t raw; 491 | 492 | if (i < 0) 493 | return i; 494 | 495 | raw = r32(ufat_cache_data(uf, i) + r * 4) & UFAT_CLUSTER_MASK; 496 | 497 | if (raw >= 0xffffff8) { 498 | *out = UFAT_CLUSTER_EOC; 499 | return 0; 500 | } 501 | 502 | if (raw == 0xffffff7) { 503 | *out = UFAT_CLUSTER_BAD; 504 | return 0; 505 | } 506 | 507 | *out = raw; 508 | return 0; 509 | } 510 | 511 | int ufat_read_fat(struct ufat *uf, ufat_cluster_t index, 512 | ufat_cluster_t *out) 513 | { 514 | if (index >= uf->bpb.num_clusters) 515 | return -UFAT_ERR_INVALID_CLUSTER; 516 | 517 | switch (uf->bpb.type) { 518 | case UFAT_TYPE_FAT12: return read_fat12(uf, index, out); 519 | case UFAT_TYPE_FAT16: return read_fat16(uf, index, out); 520 | case UFAT_TYPE_FAT32: return read_fat32(uf, index, out); 521 | } 522 | 523 | return 0; 524 | } 525 | 526 | static int write_fat_byte(struct ufat *uf, unsigned int offset, 527 | uint8_t byte, uint8_t mask) 528 | { 529 | ufat_block_t b = offset >> uf->dev->log2_block_size; 530 | unsigned int r = offset & ((1 << uf->dev->log2_block_size) - 1); 531 | int idx; 532 | uint8_t *data; 533 | 534 | idx = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 535 | if (idx < 0) 536 | return idx; 537 | 538 | data = ufat_cache_data(uf, idx); 539 | ufat_cache_write(uf, idx); 540 | 541 | data[r] = (data[r] & ~mask) | (byte & mask); 542 | return 0; 543 | } 544 | 545 | static int write_fat12(struct ufat *uf, ufat_cluster_t index, 546 | ufat_cluster_t in) 547 | { 548 | unsigned int offset = (index >> 1) * 3; 549 | int err; 550 | 551 | if (index & 1) { 552 | err = write_fat_byte(uf, offset + 1, 553 | (in & 0xf) << 4, 0xf0); 554 | if (err < 0) 555 | return err; 556 | 557 | return write_fat_byte(uf, offset + 2, 558 | (in & 0xff0) >> 4, 0xff); 559 | } 560 | 561 | err = write_fat_byte(uf, offset, in & 0xff, 0xff); 562 | if (err < 0) 563 | return err; 564 | 565 | return write_fat_byte(uf, offset + 1, (in & 0xf00) >> 8, 0x0f); 566 | } 567 | 568 | static int write_fat16(struct ufat *uf, ufat_cluster_t index, 569 | ufat_cluster_t in) 570 | { 571 | const unsigned int shift = uf->dev->log2_block_size - 1; 572 | const unsigned int b = index >> shift; 573 | const unsigned int r = index & ((1 << shift) - 1); 574 | int i = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 575 | 576 | if (i < 0) 577 | return i; 578 | 579 | ufat_cache_write(uf, i); 580 | w16(ufat_cache_data(uf, i) + r * 2, in & 0xffff); 581 | 582 | return 0; 583 | } 584 | 585 | static int write_fat32(struct ufat *uf, ufat_cluster_t index, 586 | ufat_cluster_t in) 587 | { 588 | const unsigned int shift = uf->dev->log2_block_size - 2; 589 | const unsigned int b = index >> shift; 590 | const unsigned int r = index & ((1 << shift) - 1); 591 | int i = ufat_cache_open(uf, uf->bpb.fat_start + b, 0); 592 | 593 | if (i < 0) 594 | return i; 595 | 596 | ufat_cache_write(uf, i); 597 | w32(ufat_cache_data(uf, i) + r * 4, in); 598 | 599 | return 0; 600 | } 601 | 602 | int ufat_write_fat(struct ufat *uf, ufat_cluster_t index, 603 | ufat_cluster_t in) 604 | { 605 | if (index >= uf->bpb.num_clusters) 606 | return -UFAT_ERR_INVALID_CLUSTER; 607 | 608 | switch (uf->bpb.type) { 609 | case UFAT_TYPE_FAT12: return write_fat12(uf, index, in); 610 | case UFAT_TYPE_FAT16: return write_fat16(uf, index, in); 611 | case UFAT_TYPE_FAT32: return write_fat32(uf, index, in); 612 | } 613 | 614 | return 0; 615 | } 616 | 617 | int ufat_free_chain(struct ufat *uf, ufat_cluster_t c) 618 | { 619 | while (UFAT_CLUSTER_IS_PTR(c)) { 620 | ufat_cluster_t next; 621 | int i = ufat_read_fat(uf, c, &next); 622 | 623 | if (i < 0) 624 | return i; 625 | 626 | i = ufat_write_fat(uf, c, UFAT_CLUSTER_FREE); 627 | if (i < 0) 628 | return i; 629 | 630 | c = next; 631 | } 632 | 633 | return 0; 634 | } 635 | 636 | static int alloc_cluster(struct ufat *uf, ufat_cluster_t *out, 637 | ufat_cluster_t tail) 638 | { 639 | const unsigned int total = uf->bpb.num_clusters - 2; 640 | unsigned int i; 641 | 642 | for (i = 0; i < total; i++) { 643 | const ufat_cluster_t idx = uf->alloc_ptr + 2; 644 | ufat_cluster_t c; 645 | 646 | uf->alloc_ptr = (uf->alloc_ptr + 1) % total; 647 | 648 | /* Never use this cluster index in a FAT12 system */ 649 | if (idx == 0xff0 && uf->bpb.type == UFAT_TYPE_FAT12) 650 | continue; 651 | 652 | if (!ufat_read_fat(uf, idx, &c) && c == UFAT_CLUSTER_FREE) { 653 | int err = ufat_write_fat(uf, idx, tail); 654 | 655 | if (err < 0) 656 | return err; 657 | 658 | *out = idx; 659 | return 0; 660 | } 661 | } 662 | 663 | return -UFAT_ERR_NO_CLUSTERS; 664 | } 665 | 666 | int ufat_alloc_chain(struct ufat *uf, unsigned int count, ufat_cluster_t *out) 667 | { 668 | ufat_cluster_t chain = UFAT_CLUSTER_EOC; 669 | 670 | while (count) { 671 | int err = alloc_cluster(uf, &chain, chain); 672 | 673 | if (err < 0) { 674 | ufat_free_chain(uf, chain); 675 | return err; 676 | } 677 | 678 | count--; 679 | } 680 | 681 | *out = chain; 682 | return 0; 683 | } 684 | -------------------------------------------------------------------------------- /ufat.h: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #ifndef UFAT_H_ 35 | #define UFAT_H_ 36 | 37 | #include 38 | 39 | #ifdef __cplusplus 40 | extern "C" 41 | { 42 | #endif /* def __cplusplus */ 43 | 44 | /** Block counts and indices are held in this type. */ 45 | typedef unsigned long long ufat_block_t; 46 | 47 | #define UFAT_BLOCK_NONE ((ufat_block_t)0xffffffffffffffffLL) 48 | 49 | /** 50 | * This structure is the interface to a block device. All fields must be 51 | * provided. 52 | */ 53 | 54 | struct ufat_device { 55 | /** Base-2 logarithm of size of block, bytes */ 56 | unsigned int log2_block_size; 57 | /** 58 | * Pointer to function used to read data from block device. Should 59 | * return 0 on success or -1 if an error occurs. 60 | */ 61 | int (*read)(const struct ufat_device *dev, ufat_block_t start, 62 | ufat_block_t count, void *buffer); 63 | /** 64 | * Pointer to function used to write data to block device. Should 65 | * return 0 on success or -1 if an error occurs. 66 | */ 67 | int (*write)(const struct ufat_device *dev, ufat_block_t start, 68 | ufat_block_t count, const void *buffer); 69 | }; 70 | 71 | /* Cache parameters. The more cache is used, the fewer filesystem reads/writes 72 | * have to be performed. The cache must be able to hold at least one block. 73 | */ 74 | #define UFAT_CACHE_MAX_BLOCKS 16 75 | #define UFAT_CACHE_BYTES 8192 76 | 77 | #define UFAT_CACHE_FLAG_DIRTY 0x01 78 | #define UFAT_CACHE_FLAG_PRESENT 0x02 79 | 80 | struct ufat_cache_desc { 81 | int flags; 82 | unsigned int seq; 83 | ufat_block_t index; 84 | }; 85 | 86 | /** Performance accounting statistics. */ 87 | struct ufat_stat { 88 | unsigned int read; 89 | unsigned int write; 90 | 91 | unsigned int read_blocks; 92 | unsigned int write_blocks; 93 | 94 | unsigned int cache_hit; 95 | unsigned int cache_miss; 96 | unsigned int cache_write; 97 | unsigned int cache_flush; 98 | }; 99 | 100 | typedef uint32_t ufat_cluster_t; 101 | typedef uint32_t ufat_size_t; 102 | 103 | #define UFAT_CLUSTER_FREE ((ufat_cluster_t)0) 104 | #define UFAT_CLUSTER_RESERVED ((ufat_cluster_t)1) 105 | #define UFAT_CLUSTER_BAD ((ufat_cluster_t)0xffffff7) 106 | #define UFAT_CLUSTER_EOC ((ufat_cluster_t)0xffffff8) 107 | #define UFAT_CLUSTER_IS_PTR(c) ((c) >= 2 && (c) < 0xffffff0) 108 | 109 | typedef enum { 110 | UFAT_TYPE_FAT12 = 12, 111 | UFAT_TYPE_FAT16 = 16, 112 | UFAT_TYPE_FAT32 = 32 113 | } ufat_fat_type_t; 114 | 115 | /** Data read/calculated from the BIOS Parameter Block (read-only) */ 116 | struct ufat_bpb { 117 | ufat_fat_type_t type; 118 | unsigned int log2_blocks_per_cluster; 119 | 120 | ufat_block_t fat_start; 121 | ufat_block_t fat_size; 122 | unsigned int fat_count; 123 | 124 | ufat_block_t cluster_start; 125 | ufat_cluster_t num_clusters; 126 | 127 | ufat_block_t root_start; 128 | ufat_block_t root_size; 129 | ufat_cluster_t root_cluster; 130 | }; 131 | 132 | /** This structure holds the data for an open filesystem. */ 133 | struct ufat { 134 | const struct ufat_device *dev; 135 | 136 | struct ufat_stat stat; 137 | struct ufat_bpb bpb; 138 | 139 | unsigned int next_seq; 140 | unsigned int cache_size; 141 | ufat_cluster_t alloc_ptr; 142 | 143 | struct ufat_cache_desc cache_desc[UFAT_CACHE_MAX_BLOCKS]; 144 | uint8_t cache_data[UFAT_CACHE_BYTES]; 145 | }; 146 | 147 | /** Error codes. */ 148 | typedef enum { 149 | UFAT_OK = 0, 150 | UFAT_ERR_IO, 151 | UFAT_ERR_BLOCK_SIZE, 152 | UFAT_ERR_INVALID_BPB, 153 | UFAT_ERR_BLOCK_ALIGNMENT, 154 | UFAT_ERR_INVALID_CLUSTER, 155 | UFAT_ERR_NAME_TOO_LONG, 156 | UFAT_ERR_NOT_DIRECTORY, 157 | UFAT_ERR_NOT_FILE, 158 | UFAT_ERR_IMMUTABLE, 159 | UFAT_ERR_DIRECTORY_NOT_EMPTY, 160 | UFAT_ERR_ILLEGAL_NAME, 161 | UFAT_ERR_FILE_EXISTS, 162 | UFAT_ERR_BAD_ENCODING, 163 | UFAT_ERR_DIRECTORY_FULL, 164 | UFAT_ERR_NO_CLUSTERS, 165 | UFAT_MAX_ERR 166 | } ufat_error_t; 167 | 168 | /** 169 | * \brief Translates uFAT error code into human-readable string. 170 | * 171 | * \param [in] err is an uFAT error code 172 | * 173 | * \return `err` translated to human-readable string 174 | */ 175 | 176 | const char *ufat_strerror(int err); 177 | 178 | /** 179 | * \brief Opens the filesystem. 180 | * 181 | * \pre Both `uf` and `dev` are valid pointers, they must remain valid until the 182 | * filesystem is closed. 183 | * \pre The filesystem pointed by `uf` is not opened. 184 | * 185 | * \param [out] uf is a pointer to a variable into which the filesystem will 186 | * be opened 187 | * \param [in] dev is a pointer to a block device 188 | * 189 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 190 | */ 191 | 192 | int ufat_open(struct ufat *uf, const struct ufat_device *dev); 193 | 194 | /** 195 | * \brief Synchronizes the filesystem by flushing cache. 196 | * 197 | * \pre `uf` is a valid pointer. 198 | * \pre The filesystem pointed by `uf` is opened. 199 | * 200 | * \param [in] uf is a pointer to the filesystem 201 | * 202 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 203 | */ 204 | 205 | int ufat_sync(struct ufat *uf); 206 | 207 | /** 208 | * \brief Count number of free clusters. 209 | * 210 | * The total number of available clusters is `uf->bpb.num_clusters - 2` (first 211 | * two clusters are reserved). `uf->bpb.log2_blocks_per_cluster` can be used to 212 | * convert number of clusters to number of blocks and these can be converted to 213 | * bytes by multiplying with `1 << uf->dev->log2_block_size`. 214 | * 215 | * \pre Both `uf` and `free_clusters` are valid pointers. 216 | * \pre The filesystem pointed by `uf` is opened. 217 | * 218 | * \param [in] uf is a pointer to the filesystem 219 | * \param [out] free_clusters is a pointer to a variable into which the count 220 | * of free clusters will be written 221 | * 222 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 223 | */ 224 | 225 | int ufat_count_free_clusters(struct ufat *uf, ufat_cluster_t *free_clusters); 226 | 227 | /** 228 | * \brief Closes filesystem. 229 | * 230 | * \pre `uf` is a valid pointer. 231 | * \pre The filesystem pointed by `uf` is opened. 232 | * 233 | * \post The filesystem pointed by `uf` is not opened. 234 | * 235 | * \param [in] uf is a pointer to the filesystem 236 | */ 237 | 238 | void ufat_close(struct ufat *uf); 239 | 240 | /* Directory reading. */ 241 | typedef enum { 242 | UFAT_ATTR_READONLY = 0x01, 243 | UFAT_ATTR_HIDDEN = 0x02, 244 | UFAT_ATTR_SYSTEM = 0x04, 245 | UFAT_ATTR_VOLLABEL = 0x08, 246 | UFAT_ATTR_DIRECTORY = 0x10, 247 | UFAT_ATTR_ARCHIVE = 0x20, 248 | UFAT_ATTR_LFN_FRAGMENT = 0x0f, 249 | UFAT_ATTR_USER = 0x27 250 | } ufat_attr_t; 251 | 252 | typedef uint16_t ufat_time_t; 253 | 254 | #define UFAT_TIME(h, m, s) (((h) << 11) | ((m) << 5) | ((s) >> 1)) 255 | #define UFAT_TIME_H(t) (((t) >> 11) & 0x1f) 256 | #define UFAT_TIME_M(t) (((t) >> 5) & 0x3f) 257 | #define UFAT_TIME_S(t) (((t) & 0x1f) << 1) 258 | 259 | typedef uint16_t ufat_date_t; 260 | 261 | #define UFAT_DATE(y, m, d) ((((y) - 1980) << 9) | ((m) << 5) | (d)) 262 | #define UFAT_DATE_Y(d) (((d) >> 9) + 1980) 263 | #define UFAT_DATE_M(d) (((d) >> 5) & 0xf) 264 | #define UFAT_DATE_D(d) ((d) & 0x1f) 265 | 266 | struct ufat_dirent { 267 | ufat_block_t dirent_block; 268 | unsigned int dirent_pos; 269 | 270 | /* Start of LFN fragment chain, if any */ 271 | ufat_block_t lfn_block; 272 | unsigned int lfn_pos; 273 | 274 | char short_name[9]; 275 | char short_ext[4]; 276 | 277 | ufat_attr_t attributes; 278 | ufat_date_t create_date; 279 | ufat_time_t create_time; 280 | ufat_date_t modify_date; 281 | ufat_time_t modify_time; 282 | ufat_date_t access_date; 283 | 284 | ufat_cluster_t first_cluster; 285 | ufat_size_t file_size; 286 | }; 287 | 288 | struct ufat_directory { 289 | struct ufat *uf; 290 | unsigned int cur_pos; 291 | ufat_block_t cur_block; 292 | ufat_block_t start; 293 | }; 294 | 295 | #define UFAT_LFN_MAX_CHARS 255 296 | #define UFAT_LFN_MAX_UTF8 768 297 | 298 | /** 299 | * \brief Opens root directory. 300 | * 301 | * \pre Both `uf` and `dir` are valid pointers. 302 | * \pre The filesystem pointed by `uf` is opened. 303 | * 304 | * \post The directory pointed by `dir` is opened. 305 | * 306 | * \param [in] uf is a pointer to the filesystem 307 | * \param [out] dir is a pointer to a variable into which root directory will be 308 | * opened 309 | */ 310 | 311 | void ufat_open_root(struct ufat *uf, struct ufat_directory *dir); 312 | 313 | /** 314 | * \brief Opens subdirectory. 315 | * 316 | * \pre `uf`, `dir` and `ent` are valid pointers. 317 | * \pre The filesystem pointed by `uf` is opened. 318 | * \pre Directory entry pointed by `ent` is a result of a successful 319 | * create/find/move/read operation. 320 | * 321 | * \param [in] uf is a pointer to the filesystem 322 | * \param [out] dir is a pointer to a variable into which the subdirectory will 323 | * be opened 324 | * \param [in] ent is a pointer to a directory entry representing the 325 | * subdirectory which will be opened 326 | * 327 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 328 | */ 329 | 330 | int ufat_open_subdir(struct ufat *uf, struct ufat_directory *dir, 331 | const struct ufat_dirent *ent); 332 | 333 | /** 334 | * \brief Rewinds directory position. 335 | * 336 | * \pre `dir` is a valid pointer. 337 | * \pre The directory pointed by `dir` is opened. 338 | * 339 | * \post Position of directory pointed by `dir` is rewound. 340 | * 341 | * \param [in] dir is a pointer to a directory 342 | */ 343 | 344 | void ufat_dir_rewind(struct ufat_directory *dir); 345 | 346 | /** 347 | * \brief Reads one entry from the directory. 348 | * 349 | * \pre Both `dir` and `inf` are valid pointers. 350 | * \pre The directory pointed by `dir` is opened. 351 | * 352 | * \param [in] dir is a pointer to a directory 353 | * \param [out] inf is a pointer to a variable into which the directory entry 354 | * will be read 355 | * \param [out] name_buf is the buffer for long filename, `NULL` if long 356 | * filename is not needed 357 | * \param [in] max_len is the size of `name_buf`, bytes, ignored if 358 | * `name_buf == NULL` 359 | * 360 | * \return 0 on success, 1 if end of `dir` was reached, negative error code 361 | * (`ufat_error_t`) otherwise 362 | */ 363 | 364 | int ufat_dir_read(struct ufat_directory *dir, struct ufat_dirent *inf, 365 | char *name_buf, int max_len); 366 | 367 | /** 368 | * \brief Deletes file or directory. 369 | * 370 | * \pre Both `uf` and `ent` are valid pointers. 371 | * \pre The filesystem pointed by `uf` is opened. 372 | * \pre Directory entry pointed by `ent` is a result of a successful 373 | * create/find/move/read operation. 374 | * 375 | * \param [in] uf is a pointer to the filesystem 376 | * \param [in] ent is a pointer to a directory entry representing the 377 | * file/directory which will be deleted 378 | * 379 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 380 | */ 381 | 382 | int ufat_dir_delete(struct ufat *uf, struct ufat_dirent *ent); 383 | 384 | /** 385 | * \brief Creates a directory. 386 | * 387 | * \pre `dir`, `ent` and `name` are valid pointers. 388 | * \pre The directory pointed by `dir` is opened. 389 | * 390 | * \param [in] dir is a pointer to a directory 391 | * \param [out] ent is a pointer to a variable into which the directory entry of 392 | * created directory will be written 393 | * \param [in] name is a string with name of directory that will be created 394 | * 395 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 396 | */ 397 | 398 | int ufat_dir_create(struct ufat_directory *dir, struct ufat_dirent *ent, 399 | const char *name); 400 | 401 | /** 402 | * \brief Creates a file. 403 | * 404 | * \pre `dir`, `ent` and `name` are valid pointers. 405 | * \pre The directory pointed by `dir` is opened. 406 | * 407 | * \param [in] dir is a pointer to a directory 408 | * \param [out] ent is a pointer to a variable into which the directory entry of 409 | * created file will be written 410 | * \param [in] name is a string with name of file that will be created 411 | * 412 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 413 | */ 414 | 415 | int ufat_dir_mkfile(struct ufat_directory *dir, struct ufat_dirent *ent, 416 | const char *name); 417 | 418 | /** 419 | * \brief Searches for an entry by name in given directory. 420 | * 421 | * \pre `dir`, `name` and `inf` are valid pointers. 422 | * \pre The directory pointed by `dir` is opened. 423 | * 424 | * \param [in] dir is a pointer to a directory 425 | * \param [in] name is a string with name of searched-for entry 426 | * \param [out] inf is a pointer to a variable into which the found directory 427 | * entry will be written 428 | * 429 | * \return 0 on success, 1 if entry does not exist, negative error code 430 | * (`ufat_error_t`) otherwise 431 | */ 432 | 433 | int ufat_dir_find(struct ufat_directory *dir, const char *name, 434 | struct ufat_dirent *inf); 435 | 436 | /** 437 | * \brief Searches for an entry by path in given directory. 438 | * 439 | * \pre `dir`, `path` and `inf` are valid pointers. 440 | * \pre The directory pointed by `dir` is opened. 441 | * 442 | * \param [in] dir is a pointer to a directory, it will be updated to the 443 | * directory where the search terminated 444 | * \param [in] path is a string with path of searched-for entry, relative to 445 | * `dir` 446 | * \param [out] inf is a pointer to a variable into which the found directory 447 | * entry will be written 448 | * \param [out] path_out is a pointer to a variable into which the pointer to 449 | * not-processed part of `path` will be written, `NULL` if not required 450 | * 451 | * \return 0 on success, 1 if entry does not exist, negative error code 452 | * (`ufat_error_t`) otherwise 453 | */ 454 | 455 | int ufat_dir_find_path(struct ufat_directory *dir, const char *path, 456 | struct ufat_dirent *inf, const char **path_out); 457 | 458 | /** 459 | * \brief Reads canonical long filename for a directory entry. 460 | * 461 | * \pre `uf`, `ent` and `name_buf` are valid pointers. 462 | * \pre The filesystem pointed by `uf` is opened. 463 | * \pre Directory entry pointed by `ent` is a result of a successful 464 | * create/find/move/read operation. 465 | * 466 | * \param [in] uf is a pointer to the filesystem 467 | * \param [in] ent is a pointer to a directory entry for which the canonical 468 | * long filename will be read 469 | * \param [out] name_buf is the buffer for canonical long filename 470 | * \param [in] max_len is the size of `name_buf`, bytes 471 | * 472 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 473 | */ 474 | 475 | int ufat_get_filename(struct ufat *uf, const struct ufat_dirent *ent, 476 | char *name_buf, int max_len); 477 | 478 | /** 479 | * \brief Alters the dates, times and attributes of a directory entry. 480 | * 481 | * \pre Both `uf` and `ent` are valid pointers. 482 | * \pre The filesystem pointed by `uf` is opened. 483 | * \pre Directory entry pointed by `ent` is a result of a successful 484 | * create/find/move/read operation. 485 | * 486 | * \param [in] uf is a pointer to the filesystem 487 | * \param [in] ent is a pointer to a directory entry for which attributes will 488 | * be updated 489 | * 490 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 491 | */ 492 | 493 | int ufat_update_attributes(struct ufat *uf, struct ufat_dirent *ent); 494 | 495 | /** 496 | * \brief Removes a file or directory from its containing directory and 497 | * reinserts it in (possibly the same) directory. 498 | * 499 | * \pre `ent`, `dst` and `new_name` are valid pointers. 500 | * \pre Directory entry pointed by `ent` is a result of a successful 501 | * create/find/move/read operation. 502 | * \pre Directory pointed by `dst` is opened. 503 | * 504 | * \param [in] ent is a pointer to a directory entry which will be moved, it 505 | * will be modified after successful operation 506 | * \param [in] dst is a pointer to directory to which `ent` will be moved 507 | * \param [in] new_name is a string with new name for `ent` 508 | * 509 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 510 | */ 511 | 512 | int ufat_move(struct ufat_dirent *ent, struct ufat_directory *dst, 513 | const char *new_name); 514 | 515 | /* File IO */ 516 | struct ufat_file { 517 | struct ufat *uf; 518 | 519 | ufat_block_t dirent_block; 520 | unsigned int dirent_pos; 521 | 522 | ufat_cluster_t start; 523 | ufat_size_t file_size; 524 | 525 | ufat_cluster_t prev_cluster; 526 | 527 | ufat_cluster_t cur_cluster; 528 | ufat_size_t cur_pos; 529 | }; 530 | 531 | /** 532 | * \brief Opens a file. 533 | * 534 | * \pre `uf`, `f` and `ent` are valid pointers. 535 | * \pre The filesystem pointed by `uf` is opened. 536 | * \pre Directory entry pointed by `ent` is a result of a successful 537 | * create/find/move/read operation. 538 | * 539 | * \param [in] uf is a pointer to the filesystem 540 | * \param [out] f is a pointer to a variable into which the file will be opened 541 | * \param [in] ent is a pointer to a directory entry of file which will be 542 | * opened 543 | * 544 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 545 | */ 546 | 547 | int ufat_open_file(struct ufat *uf, struct ufat_file *f, 548 | const struct ufat_dirent *ent); 549 | 550 | /** 551 | * \brief Rewinds file position. 552 | * 553 | * \pre `f` is a valid pointer. 554 | * \pre File pointed by `f` is opened. 555 | * 556 | * \post Position of file pointed by `f` is rewound. 557 | * 558 | * \param [in] f is a pointer to a file 559 | */ 560 | 561 | void ufat_file_rewind(struct ufat_file *f); 562 | 563 | /** 564 | * \brief Advances file position. 565 | * 566 | * \pre `f` is a valid pointer. 567 | * \pre File pointed by `f` is opened. 568 | * 569 | * \param [in] f is a pointer to a file 570 | * \param [in] nbytes is the number of bytes by which file position will be 571 | * advanced 572 | * 573 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 574 | */ 575 | 576 | int ufat_file_advance(struct ufat_file *f, ufat_size_t nbytes); 577 | 578 | /** 579 | * \brief Reads data from file. 580 | * 581 | * \pre Both `f` and `buf` are valid pointers. 582 | * \pre File pointed by `f` is opened. 583 | * 584 | * \param [in] f is a pointer to a file 585 | * \param [out] buf is a pointer to a buffer into which the data will be read 586 | * \param [in] max_size is the number of bytes to read 587 | * 588 | * \return number of read bytes on success, negative error code (`ufat_error_t`) 589 | * otherwise 590 | */ 591 | 592 | int ufat_file_read(struct ufat_file *f, void *buf, ufat_size_t max_size); 593 | 594 | /** 595 | * \brief Writes data to file. 596 | * 597 | * \pre Both `f` and `buf` are valid pointers. 598 | * \pre File pointed by `f` is opened. 599 | * 600 | * \param [in] f is a pointer to a file 601 | * \param [in] buf is a pointer to a buffer with data that will be written 602 | * \param [in] len is the number of bytes to write 603 | * 604 | * \return number of written bytes on success, negative error code 605 | * (`ufat_error_t`) otherwise 606 | */ 607 | 608 | int ufat_file_write(struct ufat_file *f, const void *buf, ufat_size_t len); 609 | 610 | /** 611 | * \brief Truncates file. 612 | * 613 | * \pre `f` is a valid pointer. 614 | * \pre File pointed by `f` is opened. 615 | * 616 | * \param [in] f is a pointer to a file 617 | * 618 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 619 | */ 620 | 621 | int ufat_file_truncate(struct ufat_file *f); 622 | 623 | /** 624 | * \brief Creates filesystem on block device. 625 | * 626 | * \pre `dev` is a valid pointer. 627 | * 628 | * \param [in] dev is a pointer to a block device 629 | * \param [in] nblk is the number of blocks on `dev` 630 | * 631 | * \return 0 on success, negative error code (`ufat_error_t`) otherwise 632 | */ 633 | 634 | int ufat_mkfs(struct ufat_device *dev, ufat_block_t nblk); 635 | 636 | #ifdef __cplusplus 637 | } /* extern "C" */ 638 | #endif /* def __cplusplus */ 639 | 640 | #endif 641 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* uFAT -- small flexible VFAT implementation 2 | * Copyright (C) 2012 TracMap Holdings Ltd 3 | * 4 | * Author: Daniel Beer , www.dlbeer.co.nz 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions are 8 | * met: 9 | * 10 | * 1. Redistributions of source code must retain the above copyright 11 | * notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its 18 | * contributors may be used to endorse or promote products derived from 19 | * this software without specific prior written permission. 20 | * 21 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22 | * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 23 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 24 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 | * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 27 | * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 | */ 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include "ufat.h" 43 | 44 | struct command; 45 | 46 | #define OPTION_STATISTICS 0x01 47 | #define OPTION_RANDOMIZE 0x02 48 | #define OPTION_MKFS 0x04 49 | 50 | struct options { 51 | int flags; 52 | unsigned int log2_bs; 53 | unsigned int seed; 54 | ufat_block_t num_blocks; 55 | 56 | const char *in_file; 57 | const char *out_file; 58 | 59 | const char *filename; 60 | const struct command *command; 61 | char **argv; 62 | int argc; 63 | }; 64 | 65 | struct command { 66 | const char *name; 67 | int (*func)(struct ufat *uf, 68 | const struct options *opt); 69 | }; 70 | 71 | struct file_device { 72 | struct ufat_device base; 73 | FILE *f; 74 | int is_read_only; 75 | }; 76 | 77 | static int file_device_read(const struct ufat_device *dev, ufat_block_t start, 78 | ufat_block_t count, void *buffer) 79 | { 80 | struct file_device *f = (struct file_device *)dev; 81 | 82 | if (fseek(f->f, start << f->base.log2_block_size, SEEK_SET) < 0) { 83 | perror("file_device_read: fseek"); 84 | return -1; 85 | } 86 | 87 | if (fread(buffer, 1 << f->base.log2_block_size, 88 | count, f->f) != count) { 89 | if (feof(f->f)) { 90 | memset(buffer, 0xcc, 91 | (1 << f->base.log2_block_size) * count); 92 | } else { 93 | perror("file_device_read: fread"); 94 | return -1; 95 | } 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | static int file_device_write(const struct ufat_device *dev, ufat_block_t start, 102 | ufat_block_t count, const void *buffer) 103 | { 104 | struct file_device *f = (struct file_device *)dev; 105 | 106 | if (f->is_read_only) { 107 | fprintf(stderr, "file_device_write: read-only device\n"); 108 | return -1; 109 | } 110 | 111 | if (fseek(f->f, start << f->base.log2_block_size, SEEK_SET) < 0) { 112 | perror("file_device_write: fseek"); 113 | return -1; 114 | } 115 | 116 | if (fwrite(buffer, 1 << f->base.log2_block_size, 117 | count, f->f) != count) { 118 | perror("file_device_write: fwrite"); 119 | return -1; 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | static int file_device_open(struct file_device *dev, const char *fname, 126 | unsigned int log2_bs, int create) 127 | { 128 | dev->base.log2_block_size = log2_bs; 129 | dev->base.read = file_device_read; 130 | dev->base.write = file_device_write; 131 | dev->is_read_only = 0; 132 | 133 | if (create) { 134 | dev->f = fopen(fname, "wb+"); 135 | } else { 136 | dev->f = fopen(fname, "rb+"); 137 | if (!dev->f && errno == EACCES) { 138 | dev->is_read_only = 1; 139 | dev->f = fopen(fname, "rb"); 140 | } 141 | } 142 | 143 | if (!dev->f) { 144 | perror("open"); 145 | return -1; 146 | } 147 | 148 | return 0; 149 | } 150 | 151 | static void file_device_close(struct file_device *dev) 152 | { 153 | fclose(dev->f); 154 | } 155 | 156 | static FILE *open_input(const char *fname) 157 | { 158 | FILE *in; 159 | 160 | if (!fname) 161 | return stdin; 162 | 163 | in = fopen(fname, "rb"); 164 | if (!in) { 165 | fprintf(stderr, "Failed to open %s for reading: %s\n", 166 | fname, strerror(errno)); 167 | return NULL; 168 | } 169 | 170 | return in; 171 | } 172 | 173 | static void close_input(const char *fname, FILE *f) 174 | { 175 | if (fname) 176 | fclose(f); 177 | } 178 | 179 | static FILE *open_output(const char *fname) 180 | { 181 | FILE *out; 182 | 183 | if (!fname) 184 | return stdout; 185 | 186 | out = fopen(fname, "wb"); 187 | if (!out) { 188 | fprintf(stderr, "Failed to open %s for writing: %s\n", 189 | fname, strerror(errno)); 190 | return NULL; 191 | } 192 | 193 | return out; 194 | } 195 | 196 | static int close_output(const char *fname, FILE *f) 197 | { 198 | if (fname && fclose(f) < 0) { 199 | fprintf(stderr, "Error on closing %s: %s\n", 200 | fname, strerror(errno)); 201 | return -1; 202 | } 203 | 204 | return 0; 205 | } 206 | 207 | static void print_date(FILE *out, ufat_date_t d) 208 | { 209 | fprintf(out, "%04d-%02d-%02d", 210 | UFAT_DATE_Y(d), UFAT_DATE_M(d), UFAT_DATE_D(d)); 211 | } 212 | 213 | static void print_time(FILE *out, ufat_time_t t) 214 | { 215 | fprintf(out, "%2d:%02d:%02d", 216 | UFAT_TIME_H(t), UFAT_TIME_M(t), UFAT_TIME_S(t)); 217 | } 218 | 219 | static void print_attributes(FILE *out, ufat_attr_t a) 220 | { 221 | fprintf(out, "%c%c%c%c%c", 222 | (a & UFAT_ATTR_ARCHIVE) ? 'A' : ' ', 223 | (a & UFAT_ATTR_SYSTEM) ? 'S' : ' ', 224 | (a & UFAT_ATTR_HIDDEN) ? 'H' : ' ', 225 | (a & UFAT_ATTR_READONLY) ? 'R' : ' ', 226 | (a & UFAT_ATTR_DIRECTORY) ? 'D' : ' '); 227 | } 228 | 229 | static int list_dir(FILE *out, struct ufat_directory *dir) 230 | { 231 | for (;;) { 232 | struct ufat_dirent inf; 233 | char lfn[UFAT_LFN_MAX_UTF8]; 234 | int err; 235 | 236 | err = ufat_dir_read(dir, &inf, lfn, sizeof(lfn)); 237 | if (err < 0) { 238 | fprintf(stderr, "list_dir: ufat_dir_read: %s\n", 239 | ufat_strerror(err)); 240 | return err; 241 | } 242 | 243 | if (err) 244 | break; 245 | 246 | print_date(out, inf.modify_date); 247 | fprintf(out, " "); 248 | print_time(out, inf.modify_time); 249 | fprintf(out, " "); 250 | print_attributes(out, inf.attributes & ~UFAT_ATTR_DIRECTORY); 251 | if (inf.attributes & UFAT_ATTR_DIRECTORY) 252 | fprintf(out, " %9s", ""); 253 | else 254 | fprintf(out, " %9u", inf.file_size); 255 | fprintf(out, " %s\n", lfn); 256 | } 257 | 258 | return 0; 259 | } 260 | 261 | static int find_path(struct ufat *uf, 262 | struct ufat_directory *dir, const char *path, 263 | struct ufat_dirent *inf, const char **path_out) 264 | { 265 | int err; 266 | 267 | ufat_open_root(uf, dir); 268 | err = ufat_dir_find_path(dir, path, inf, path_out); 269 | 270 | if (err < 0) 271 | fprintf(stderr, "ufat_dir_find_path: %s\n", 272 | ufat_strerror(err)); 273 | 274 | return err; 275 | } 276 | 277 | static int require_file(struct ufat *uf, const char *path, 278 | struct ufat_dirent *ent) 279 | { 280 | struct ufat_directory dir; 281 | int err = find_path(uf, &dir, path, ent, NULL); 282 | 283 | if (err < 0) 284 | return err; 285 | 286 | if (err) { 287 | fprintf(stderr, "No such file or directory: %s\n", path); 288 | return -1; 289 | } 290 | 291 | return 0; 292 | } 293 | 294 | static int cmd_dir(struct ufat *uf, const struct options *opt) 295 | { 296 | struct ufat_directory dir; 297 | FILE *out; 298 | 299 | if (opt->argc) { 300 | struct ufat_dirent ent; 301 | int err; 302 | 303 | if (require_file(uf, opt->argv[0], &ent) < 0) 304 | return -1; 305 | 306 | err = ufat_open_subdir(uf, &dir, &ent); 307 | if (err < 0) { 308 | fprintf(stderr, "ufat_open_subdir: %s\n", 309 | ufat_strerror(err)); 310 | return -1; 311 | } 312 | } else { 313 | ufat_open_root(uf, &dir); 314 | } 315 | 316 | out = open_output(opt->out_file); 317 | if (!out) 318 | return -1; 319 | 320 | if (list_dir(out, &dir) < 0) { 321 | close_output(opt->out_file, out); 322 | return -1; 323 | } 324 | 325 | return close_output(opt->out_file, out); 326 | } 327 | 328 | static int cmd_fstat(struct ufat *uf, const struct options *opt) 329 | { 330 | FILE *out; 331 | struct ufat_dirent ent; 332 | char can_name[UFAT_LFN_MAX_UTF8]; 333 | int err; 334 | 335 | if (!opt->argc) { 336 | fprintf(stderr, "You must specify a file path\n"); 337 | return -1; 338 | } 339 | 340 | if (require_file(uf, opt->argv[0], &ent) < 0) 341 | return -1; 342 | 343 | out = open_output(opt->out_file); 344 | if (!out) 345 | return -1; 346 | 347 | err = ufat_get_filename(uf, &ent, can_name, sizeof(can_name)); 348 | if (err < 0) 349 | fprintf(stderr, "ufat_get_filename: %s\n", 350 | ufat_strerror(err)); 351 | else 352 | fprintf(out, "Filename: %s\n", can_name); 353 | 354 | fprintf(out, "Entry block/offset: %llu/%d\n", 355 | ent.dirent_block, ent.dirent_pos); 356 | 357 | if (ent.lfn_block != UFAT_BLOCK_NONE) 358 | fprintf(out, "LFN start block/offset: %llu/%d\n", 359 | ent.lfn_block, ent.lfn_pos); 360 | 361 | fprintf(out, "Short name: %s", ent.short_name); 362 | if (ent.short_ext[0]) 363 | fprintf(out, ".%s\n", ent.short_ext); 364 | else 365 | fprintf(out, "\n"); 366 | fprintf(out, "Attributes: 0x%02x (", ent.attributes); 367 | print_attributes(out, ent.attributes); 368 | fprintf(out, ")\n"); 369 | fprintf(out, "Creation date/time: "); 370 | print_date(out, ent.create_date); 371 | fprintf(out, " "); 372 | print_time(out, ent.create_time); 373 | fprintf(out, "\n"); 374 | fprintf(out, "Modification date/time: "); 375 | print_date(out, ent.modify_date); 376 | fprintf(out, " "); 377 | print_time(out, ent.modify_time); 378 | fprintf(out, "\n"); 379 | fprintf(out, "Last access date: "); 380 | print_date(out, ent.access_date); 381 | fprintf(out, "\n"); 382 | fprintf(out, "First cluster: %u\n", ent.first_cluster); 383 | fprintf(out, "Size: %u\n", ent.file_size); 384 | 385 | return close_output(opt->out_file, out); 386 | } 387 | 388 | static int cmd_read(struct ufat *uf, const struct options *opt) 389 | { 390 | FILE *out; 391 | struct ufat_dirent ent; 392 | struct ufat_file file; 393 | int err; 394 | 395 | if (!opt->argc) { 396 | fprintf(stderr, "You must specify a file path\n"); 397 | return -1; 398 | } 399 | 400 | if (require_file(uf, opt->argv[0], &ent) < 0) 401 | return -1; 402 | 403 | err = ufat_open_file(uf, &file, &ent); 404 | if (err < 0) { 405 | fprintf(stderr, "ufat_open_file: %s\n", ufat_strerror(err)); 406 | return -1; 407 | } 408 | 409 | out = open_output(opt->out_file); 410 | if (!out) 411 | return -1; 412 | 413 | for (;;) { 414 | char buf[16384]; 415 | int req_size = sizeof(buf); 416 | int len; 417 | 418 | if (opt->flags & OPTION_RANDOMIZE) 419 | req_size = random() % sizeof(buf) + 1; 420 | 421 | len = ufat_file_read(&file, buf, req_size); 422 | if (len < 0) { 423 | fprintf(stderr, "ufat_file_read: %s\n", 424 | ufat_strerror(len)); 425 | close_output(opt->out_file, out); 426 | return -1; 427 | } 428 | 429 | if (!len) 430 | break; 431 | 432 | if (fwrite(buf, 1, len, out) != (size_t)len) { 433 | perror("fwrite"); 434 | close_output(opt->out_file, out); 435 | return -1; 436 | } 437 | } 438 | 439 | return close_output(opt->out_file, out); 440 | } 441 | 442 | static int cmd_write(struct ufat *uf, const struct options *opt) 443 | { 444 | FILE *in; 445 | const char *basename; 446 | struct ufat_directory dir; 447 | struct ufat_dirent ent; 448 | struct ufat_file file; 449 | int err; 450 | 451 | if (!opt->argc) { 452 | fprintf(stderr, "You must specify a file path\n"); 453 | return -1; 454 | } 455 | 456 | err = find_path(uf, &dir, opt->argv[0], &ent, &basename); 457 | if (err < 0) 458 | return -1; 459 | 460 | in = open_input(opt->in_file); 461 | if (!in) 462 | return -1; 463 | 464 | if (err) { 465 | time_t now = time(NULL); 466 | struct tm *local = localtime(&now); 467 | 468 | ent.attributes = 0; 469 | ent.create_date = UFAT_DATE(local->tm_year + 1900, 470 | local->tm_mon + 1, 471 | local->tm_mday); 472 | ent.create_time = UFAT_TIME(local->tm_hour, 473 | local->tm_min, 474 | local->tm_sec); 475 | ent.modify_date = ent.create_date; 476 | ent.modify_time = ent.create_time; 477 | ent.access_date = ent.create_date; 478 | 479 | err = ufat_dir_mkfile(&dir, &ent, basename); 480 | if (err < 0) { 481 | fprintf(stderr, "Failed to create file: %s: %s\n", 482 | basename, ufat_strerror(err)); 483 | close_input(opt->in_file, in); 484 | return -1; 485 | } 486 | } 487 | 488 | err = ufat_open_file(uf, &file, &ent); 489 | if (err < 0) { 490 | fprintf(stderr, "ufat_open_file: %s\n", ufat_strerror(err)); 491 | close_input(opt->in_file, in); 492 | return -1; 493 | } 494 | 495 | for (;;) { 496 | char buf[16384]; 497 | int req_size = sizeof(buf); 498 | int len; 499 | 500 | if (opt->flags & OPTION_RANDOMIZE) 501 | req_size = random() % sizeof(buf) + 1; 502 | 503 | len = fread(buf, 1, req_size, in); 504 | if (ferror(stdin)) { 505 | perror("fread"); 506 | close_input(opt->in_file, in); 507 | return -1; 508 | } 509 | 510 | if (!len) 511 | break; 512 | 513 | len = ufat_file_write(&file, buf, len); 514 | if (len < 0) { 515 | fprintf(stderr, "ufat_file_write: %s\n", 516 | ufat_strerror(len)); 517 | close_input(opt->in_file, in); 518 | return -1; 519 | } 520 | } 521 | 522 | err = ufat_file_truncate(&file); 523 | if (err < 0) { 524 | fprintf(stderr, "ufat_file_truncate: %s\n", 525 | ufat_strerror(err)); 526 | close_input(opt->in_file, in); 527 | return -1; 528 | } 529 | 530 | close_input(opt->in_file, in); 531 | return 0; 532 | } 533 | 534 | static int cmd_rm(struct ufat *uf, const struct options *opt) 535 | { 536 | struct ufat_dirent ent; 537 | int err; 538 | 539 | if (!opt->argc) { 540 | fprintf(stderr, "You must specify a file path\n"); 541 | return -1; 542 | } 543 | 544 | if (require_file(uf, opt->argv[0], &ent) < 0) 545 | return -1; 546 | 547 | err = ufat_dir_delete(uf, &ent); 548 | if (err < 0) { 549 | fprintf(stderr, "ufat_dir_delete: %s\n", ufat_strerror(err)); 550 | return -1; 551 | } 552 | 553 | return 0; 554 | } 555 | 556 | static int cmd_mkdir(struct ufat *uf, const struct options *opt) 557 | { 558 | const char *basename; 559 | struct ufat_directory dir; 560 | struct ufat_dirent ent; 561 | struct tm *local; 562 | time_t now = time(NULL); 563 | int err; 564 | 565 | if (!opt->argc) { 566 | fprintf(stderr, "You must specify a file path\n"); 567 | return -1; 568 | } 569 | 570 | err = find_path(uf, &dir, opt->argv[0], &ent, &basename); 571 | if (err < 0) 572 | return -1; 573 | 574 | if (!err) { 575 | fprintf(stderr, "Path already exists: %s\n", 576 | opt->argv[0]); 577 | return -1; 578 | } 579 | 580 | local = localtime(&now); 581 | ent.attributes = 0; 582 | ent.create_date = UFAT_DATE(local->tm_year + 1900, local->tm_mon + 1, 583 | local->tm_mday); 584 | ent.create_time = UFAT_TIME(local->tm_hour, local->tm_min, 585 | local->tm_sec); 586 | ent.modify_date = ent.create_date; 587 | ent.modify_time = ent.create_time; 588 | ent.access_date = ent.create_date; 589 | 590 | err = ufat_dir_create(&dir, &ent, basename); 591 | if (err < 0) { 592 | fprintf(stderr, "ufat_dir_create: %s\n", ufat_strerror(err)); 593 | return -1; 594 | } 595 | 596 | return 0; 597 | } 598 | 599 | static int parse_dosattr(const char *attr, ufat_attr_t *a) 600 | { 601 | ufat_attr_t out = 0; 602 | 603 | while (*attr) { 604 | switch (*attr) { 605 | case 'A': 606 | case 'a': 607 | out |= UFAT_ATTR_ARCHIVE; 608 | break; 609 | 610 | case 'H': 611 | case 'h': 612 | out |= UFAT_ATTR_HIDDEN; 613 | break; 614 | 615 | case 'R': 616 | case 'r': 617 | out |= UFAT_ATTR_READONLY; 618 | break; 619 | 620 | case 'S': 621 | case 's': 622 | out |= UFAT_ATTR_SYSTEM; 623 | break; 624 | 625 | default: 626 | fprintf(stderr, "Unrecognized DOS attribute: %c\n", 627 | *attr); 628 | return -1; 629 | } 630 | 631 | attr++; 632 | } 633 | 634 | *a = out; 635 | return 0; 636 | } 637 | 638 | static int parse_date(const char *text, ufat_date_t *d) 639 | { 640 | int bits[3] = {0}; 641 | int b = 0; 642 | int i; 643 | 644 | if (!strcasecmp(text, "now")) { 645 | time_t now = time(NULL); 646 | struct tm *local = localtime(&now); 647 | 648 | *d = UFAT_DATE(local->tm_year + 1900, 649 | local->tm_mon + 1, 650 | local->tm_mday); 651 | return 0; 652 | } 653 | 654 | for (i = 0; text[i]; i++) { 655 | const int c = text[i]; 656 | 657 | if (c == '-') { 658 | b++; 659 | if (b >= 3) 660 | goto bad_format; 661 | } else if (isdigit(c)) { 662 | bits[b] = bits[b] * 10 + c - '0'; 663 | } else { 664 | goto bad_format; 665 | } 666 | } 667 | 668 | if (b < 2) 669 | goto bad_format; 670 | 671 | *d = UFAT_DATE(bits[0], bits[1], bits[2]); 672 | return 0; 673 | 674 | bad_format: 675 | fprintf(stderr, "Date must be of the form YYYY-MM-DD: %s\n", text); 676 | return -1; 677 | } 678 | 679 | static int parse_time(const char *text, ufat_time_t *t) 680 | { 681 | int bits[3] = {0}; 682 | int b = 0; 683 | int i; 684 | 685 | if (!strcasecmp(text, "now")) { 686 | time_t now = time(NULL); 687 | struct tm *local = localtime(&now); 688 | 689 | *t = UFAT_TIME(local->tm_hour, 690 | local->tm_min, 691 | local->tm_sec); 692 | return 0; 693 | } 694 | 695 | for (i = 0; text[i]; i++) { 696 | const int c = text[i]; 697 | 698 | if (c == ':') { 699 | b++; 700 | if (b >= 3) 701 | goto bad_format; 702 | } else if (isdigit(c)) { 703 | bits[b] = bits[b] * 10 + c - '0'; 704 | } else { 705 | goto bad_format; 706 | } 707 | } 708 | 709 | if (b < 2) 710 | goto bad_format; 711 | 712 | *t = UFAT_TIME(bits[0], bits[1], bits[2]); 713 | return 0; 714 | 715 | bad_format: 716 | fprintf(stderr, "Time must be of the form HH:MM:SS: %s\n", 717 | text); 718 | return -1; 719 | } 720 | 721 | static int parse_attribute(const char *spec, struct ufat_dirent *ent) 722 | { 723 | char key[64]; 724 | char *value; 725 | 726 | if (*spec == '+' || *spec == '~' || *spec == '=') { 727 | ufat_attr_t a; 728 | 729 | if (parse_dosattr(spec + 1, &a) < 0) 730 | return -1; 731 | 732 | if (*spec == '+') 733 | ent->attributes |= a; 734 | else if (*spec == '=') 735 | ent->attributes = a; 736 | else 737 | ent->attributes &= ~a; 738 | 739 | return 0; 740 | } 741 | 742 | /* Try to identify a key=value pair */ 743 | strncpy(key, spec, sizeof(key)); 744 | key[sizeof(key) - 1] = 0; 745 | 746 | value = strchr(key, '='); 747 | if (!value) { 748 | fprintf(stderr, "Unknown attribute specification: %s\n", 749 | spec); 750 | return -1; 751 | } 752 | 753 | *(value++) = 0; 754 | 755 | /* Deal with it */ 756 | if (!strcasecmp(key, "create_date")) 757 | return parse_date(value, &ent->create_date); 758 | if (!strcasecmp(key, "create_time")) 759 | return parse_time(value, &ent->create_time); 760 | if (!strcasecmp(key, "modify_date")) 761 | return parse_date(value, &ent->modify_date); 762 | if (!strcasecmp(key, "modify_time")) 763 | return parse_time(value, &ent->modify_time); 764 | if (!strcasecmp(key, "access_date")) 765 | return parse_date(value, &ent->access_date); 766 | 767 | fprintf(stderr, "Unknown attribute: %s\n", key); 768 | return -1; 769 | } 770 | 771 | static int cmd_rename(struct ufat *uf, const struct options *opt) 772 | { 773 | struct ufat_directory dir; 774 | struct ufat_dirent ent; 775 | int err; 776 | const char *src_path; 777 | const char *dst_name; 778 | 779 | if (opt->argc < 2) { 780 | fprintf(stderr, "You must specified a source path and " 781 | "a new name.\n"); 782 | return -1; 783 | } 784 | 785 | src_path = opt->argv[0]; 786 | dst_name = opt->argv[1]; 787 | 788 | err = find_path(uf, &dir, src_path, &ent, NULL); 789 | if (err < 0) 790 | return -1; 791 | 792 | if (err) { 793 | fprintf(stderr, "No such file or directory: %s\n", 794 | ufat_strerror(err)); 795 | return -1; 796 | } 797 | 798 | err = ufat_move(&ent, &dir, dst_name); 799 | if (err < 0) { 800 | fprintf(stderr, "ufat_move: %s\n", ufat_strerror(err)); 801 | return -1; 802 | } 803 | 804 | return 0; 805 | } 806 | 807 | static int cmd_move(struct ufat *uf, const struct options *opt) 808 | { 809 | struct ufat_directory dir; 810 | struct ufat_dirent src_ent; 811 | struct ufat_dirent dst_ent; 812 | int err; 813 | const char *src_path; 814 | const char *dst_path; 815 | const char *new_name; 816 | char old_name[UFAT_LFN_MAX_UTF8]; 817 | 818 | if (opt->argc < 2) { 819 | fprintf(stderr, "You must specified a source path and " 820 | "a destination path.\n"); 821 | return -1; 822 | } 823 | 824 | src_path = opt->argv[0]; 825 | dst_path = opt->argv[1]; 826 | 827 | err = find_path(uf, &dir, src_path, &src_ent, NULL); 828 | if (err < 0) 829 | return -1; 830 | 831 | if (err) { 832 | fprintf(stderr, "No such file or directory: %s\n", 833 | opt->argv[0]); 834 | return -1; 835 | } 836 | 837 | ufat_open_root(uf, &dir); 838 | err = ufat_dir_find_path(&dir, dst_path, &dst_ent, &new_name); 839 | 840 | if (err < 0) { 841 | fprintf(stderr, "ufat_dir_find_path: %s\n", 842 | ufat_strerror(err)); 843 | return -1; 844 | } 845 | 846 | if (!err) { 847 | /* Is the destination a directory or an existing file? */ 848 | if (!(dst_ent.attributes & UFAT_ATTR_DIRECTORY)) { 849 | fprintf(stderr, "File already exists: %s\n", 850 | dst_path); 851 | return -1; 852 | } 853 | 854 | /* It's a directory name. Use the old canonical filename. */ 855 | err = ufat_open_subdir(uf, &dir, &dst_ent); 856 | if (err < 0) { 857 | fprintf(stderr, "ufat_open_subdir: %s\n", 858 | ufat_strerror(err)); 859 | return -1; 860 | } 861 | 862 | err = ufat_get_filename(uf, &src_ent, 863 | old_name, sizeof(old_name)); 864 | if (err < 0) { 865 | fprintf(stderr, "ufat_get_filename: %s\n", 866 | ufat_strerror(err)); 867 | return -1; 868 | } 869 | 870 | new_name = old_name; 871 | } 872 | 873 | err = ufat_move(&src_ent, &dir, new_name); 874 | if (err < 0) { 875 | fprintf(stderr, "ufat_move: %s\n", ufat_strerror(err)); 876 | return -1; 877 | } 878 | 879 | return 0; 880 | } 881 | 882 | static int cmd_chattr(struct ufat *uf, const struct options *opt) 883 | { 884 | struct ufat_dirent ent; 885 | int err; 886 | int i; 887 | 888 | if (!opt->argc) { 889 | fprintf(stderr, "You must specify a file path\n"); 890 | return -1; 891 | } 892 | 893 | if (require_file(uf, opt->argv[0], &ent) < 0) 894 | return -1; 895 | 896 | for (i = 1; i < opt->argc; i++) 897 | if (parse_attribute(opt->argv[i], &ent) < 0) 898 | return -1; 899 | 900 | err = ufat_update_attributes(uf, &ent); 901 | if (err < 0) { 902 | fprintf(stderr, "ufat_dir_update_attributes: %s\n", 903 | ufat_strerror(err)); 904 | return -1; 905 | } 906 | 907 | return 0; 908 | } 909 | 910 | static void show_info(FILE *out, const struct ufat_bpb *bpb) 911 | { 912 | fprintf(out, "Type: FAT%d\n", bpb->type); 913 | fprintf(out, "Blocks per cluster: %u\n", 914 | 1 << bpb->log2_blocks_per_cluster); 915 | fprintf(out, "FAT size (blocks): %llu\n", bpb->fat_size); 916 | fprintf(out, "FAT offset (block): %llu\n", bpb->fat_start); 917 | fprintf(out, "FAT count: %u\n", bpb->fat_count); 918 | fprintf(out, "Cluster starting block: %llu\n", bpb->cluster_start); 919 | fprintf(out, "Clusters: %u\n", bpb->num_clusters); 920 | fprintf(out, "Root directory block start: %llu\n", bpb->root_start); 921 | fprintf(out, "Root directory block count: %llu\n", bpb->root_size); 922 | fprintf(out, "Root directory cluster: %u\n", bpb->root_cluster); 923 | } 924 | 925 | static void usage(const char *progname) 926 | { 927 | printf( 928 | "Usage: %s [options] [command [args]]\n" 929 | "\n" 930 | "Options may be any of the following:\n" 931 | " -b block-size Set the simulated block size\n" 932 | " -S Show performance statistics\n" 933 | " -R seed Randomize file IO request sizes\n" 934 | " -i filename Read input from the given file\n" 935 | " -o filename Write output to the given file\n" 936 | " --mkfs Initialize the filesystem (WARNING: all existing\n" 937 | " data will be lost)\n" 938 | " --help Show this text\n" 939 | " --version Show version information\n" 940 | "\n" 941 | "With no command, basic information is printed. Available commands are:\n" 942 | " dir [directory] Show a directory listing\n" 943 | " fstat [path] Show directory entry details\n" 944 | " read [file] Dump the contents of the given file\n" 945 | " write [file] Write to a file\n" 946 | " rm [path] Remove a directory or file\n" 947 | " mkdir [directory] Create a new empty directory\n" 948 | " chattr [path] [attributes]\n" 949 | " Alter file attributes/dates/times (see below)\n" 950 | " move [src] [dst] Move a file from one place to another\n" 951 | " rename [src] [new-name] Rename a file without moving it\n" 952 | "\n" 953 | "Attributes are specified using arguments with a key=value syntax:\n" 954 | " create_date=YYYY-MM-DD Creation date\n" 955 | " create_time=HH:MM:SS Creation time\n" 956 | " modify_date=YYYY-MM-DD Modification date\n" 957 | " modify_time=HH:MM:SS Modification time\n" 958 | " access_date=YYYY-MM-DD Access date\n" 959 | " [+/~/=]AHRS Alter archive/hidden/read-only/system\n" 960 | "\n" 961 | "The special value \"now\" can be used for either dates or times.\n", 962 | progname); 963 | } 964 | 965 | static void version_banner(void) 966 | { 967 | printf( 968 | "ufat version 20120528\n" 969 | "Copyright (C) 2012 TracMap Holdings Ltd\n" 970 | "This is free software; see the source for copying conditions. There is NO\n" 971 | "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" 972 | ); 973 | } 974 | 975 | static int parse_blocksize(const char *arg, unsigned int *out) 976 | { 977 | unsigned int target = atoi(arg); 978 | unsigned int c = 0; 979 | 980 | if (!target) { 981 | fprintf(stderr, "Block size must be greater than 0\n"); 982 | return -1; 983 | } 984 | 985 | while (target > 1) { 986 | if (target & 1) { 987 | fprintf(stderr, "Block size must be a power of 2\n"); 988 | return -1; 989 | } 990 | 991 | target >>= 1; 992 | c++; 993 | } 994 | 995 | *out = c; 996 | return 0; 997 | } 998 | 999 | static const struct command command_table[] = { 1000 | {"dir", cmd_dir}, 1001 | {"fstat", cmd_fstat}, 1002 | {"read", cmd_read}, 1003 | {"write", cmd_write}, 1004 | {"rm", cmd_rm}, 1005 | {"mkdir", cmd_mkdir}, 1006 | {"chattr", cmd_chattr}, 1007 | {"move", cmd_move}, 1008 | {"rename", cmd_rename} 1009 | }; 1010 | 1011 | static const struct command *find_command(const char *name) 1012 | { 1013 | size_t i; 1014 | 1015 | for (i = 0; i < sizeof(command_table) / 1016 | sizeof(command_table[0]); i++) { 1017 | const struct command *c = &command_table[i]; 1018 | 1019 | if (!strcasecmp(c->name, name)) 1020 | return c; 1021 | } 1022 | 1023 | return NULL; 1024 | } 1025 | 1026 | static int parse_options(int argc, char **argv, struct options *opt) 1027 | { 1028 | static const struct option longopts[] = { 1029 | {"help", 0, 0, 'H'}, 1030 | {"version", 0, 0, 'V'}, 1031 | {"mkfs", 1, 0, 'M'}, 1032 | {NULL, 0, 0, 0} 1033 | }; 1034 | int o; 1035 | 1036 | memset(opt, 0, sizeof(*opt)); 1037 | opt->log2_bs = 9; 1038 | 1039 | while ((o = getopt_long(argc, argv, "b:SR:i:o:", longopts, NULL)) >= 0) 1040 | switch (o) { 1041 | case 'i': 1042 | opt->in_file = optarg; 1043 | break; 1044 | 1045 | case 'o': 1046 | opt->out_file = optarg; 1047 | break; 1048 | 1049 | case 'M': 1050 | opt->flags |= OPTION_MKFS; 1051 | opt->num_blocks = atoll(optarg); 1052 | break; 1053 | 1054 | case 'R': 1055 | opt->flags |= OPTION_RANDOMIZE; 1056 | opt->seed = atoi(optarg); 1057 | break; 1058 | 1059 | case 'S': 1060 | opt->flags |= OPTION_STATISTICS; 1061 | break; 1062 | 1063 | case 'H': 1064 | usage(argv[0]); 1065 | exit(0); 1066 | 1067 | case 'V': 1068 | version_banner(); 1069 | exit(0); 1070 | 1071 | case 'b': 1072 | if (parse_blocksize(optarg, &opt->log2_bs) < 0) 1073 | return -1; 1074 | break; 1075 | 1076 | case '?': 1077 | fprintf(stderr, "Try --help for usage.\n"); 1078 | return -1; 1079 | } 1080 | 1081 | argc -= optind; 1082 | argv += optind; 1083 | 1084 | if (argc <= 0) { 1085 | fprintf(stderr, "Expected an image name\n"); 1086 | return -1; 1087 | } 1088 | 1089 | opt->filename = argv[0]; 1090 | 1091 | if (argc >= 2) { 1092 | opt->command = find_command(argv[1]); 1093 | opt->argv = argv + 2; 1094 | opt->argc = argc - 2; 1095 | 1096 | if (!opt->command) { 1097 | fprintf(stderr, "Unknown command: %s\n", argv[1]); 1098 | return -1; 1099 | } 1100 | } else { 1101 | opt->command = NULL; 1102 | opt->argv = NULL; 1103 | opt->argc = 0; 1104 | } 1105 | 1106 | return 0; 1107 | } 1108 | 1109 | static void dump_stats(const struct ufat_stat *st) 1110 | { 1111 | fprintf(stderr, "\n"); 1112 | fprintf(stderr, 1113 | "Reads: %6d comprising %6d blocks\n", 1114 | st->read, st->read_blocks); 1115 | fprintf(stderr, 1116 | "Writes: %6d comprising %6d blocks\n", 1117 | st->write, st->write_blocks); 1118 | 1119 | fprintf(stderr, "Cache write/flush: %6d/%6d\n", 1120 | st->cache_write, st->cache_flush); 1121 | fprintf(stderr, "Cache hit/miss: %6d/%6d", 1122 | st->cache_hit, st->cache_miss); 1123 | 1124 | if (st->cache_hit + st->cache_miss) 1125 | fprintf(stderr, " (%02d%% hit rate)\n", 1126 | st->cache_hit * 100 / (st->cache_hit + st->cache_miss)); 1127 | else 1128 | fprintf(stderr, "\n"); 1129 | } 1130 | 1131 | int main(int argc, char **argv) 1132 | { 1133 | struct file_device dev; 1134 | struct ufat uf; 1135 | struct options opt; 1136 | int err; 1137 | 1138 | if (parse_options(argc, argv, &opt) < 0) 1139 | return -1; 1140 | 1141 | srandom(opt.seed); 1142 | 1143 | if (file_device_open(&dev, opt.filename, opt.log2_bs, 1144 | opt.flags & OPTION_MKFS) < 0) 1145 | return -1; 1146 | 1147 | if (opt.flags & OPTION_MKFS) { 1148 | err = ufat_mkfs(&dev.base, opt.num_blocks); 1149 | if (err < 0) { 1150 | fprintf(stderr, "ufat_mkfs: %s\n", ufat_strerror(err)); 1151 | file_device_close(&dev); 1152 | return -1; 1153 | } 1154 | } 1155 | 1156 | err = ufat_open(&uf, &dev.base); 1157 | if (err) { 1158 | fprintf(stderr, "ufat_open: %s\n", ufat_strerror(err)); 1159 | file_device_close(&dev); 1160 | return -1; 1161 | } 1162 | 1163 | if (!opt.command) { 1164 | FILE *out = open_output(opt.out_file); 1165 | 1166 | if (!out) { 1167 | err = -1; 1168 | } else { 1169 | show_info(out, &uf.bpb); 1170 | err = close_output(opt.out_file, out); 1171 | } 1172 | } else { 1173 | err = opt.command->func(&uf, &opt); 1174 | } 1175 | 1176 | ufat_close(&uf); 1177 | file_device_close(&dev); 1178 | 1179 | if (opt.flags & OPTION_STATISTICS) 1180 | dump_stats(&uf.stat); 1181 | 1182 | return err; 1183 | } 1184 | --------------------------------------------------------------------------------