OSDN Git Service

755d0d60995a72e72ffd2ad39bab1ba5e025f743
[android-x86/external-exfat.git] / libexfat / io.c
1 /*
2         io.c (02.09.09)
3         exFAT file system implementation library.
4
5         Copyright (C) 2010-2012  Andrew Nayenko
6
7         This program is free software: you can redistribute it and/or modify
8         it under the terms of the GNU General Public License as published by
9         the Free Software Foundation, either version 3 of the License, or
10         (at your option) any later version.
11
12         This program is distributed in the hope that it will be useful,
13         but WITHOUT ANY WARRANTY; without even the implied warranty of
14         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15         GNU General Public License for more details.
16
17         You should have received a copy of the GNU General Public License
18         along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "exfat.h"
22 #include <inttypes.h>
23 #include <sys/types.h>
24 #include <sys/uio.h>
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28 #include <string.h>
29 #ifdef USE_UBLIO
30 #include <sys/uio.h>
31 #include <ublio.h>
32 #endif
33
34 #if !defined(_FILE_OFFSET_BITS) || (_FILE_OFFSET_BITS != 64)
35         #error You should define _FILE_OFFSET_BITS=64
36 #endif
37
38 struct exfat_dev
39 {
40         int fd;
41         enum exfat_mode mode;
42 #ifdef USE_UBLIO
43         off_t pos;
44         ublio_filehandle_t ufh;
45 #endif
46 };
47
48 struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
49 {
50         struct exfat_dev* dev;
51         struct stat stbuf;
52 #ifdef USE_UBLIO
53         struct ublio_param up;
54 #endif
55
56         dev = malloc(sizeof(struct exfat_dev));
57         if (dev == NULL)
58         {
59                 exfat_error("failed to allocate memory for device structure");
60                 return NULL;
61         }
62
63         switch (mode)
64         {
65         case EXFAT_MODE_RO:
66                 dev->fd = open(spec, O_RDONLY);
67                 if (dev->fd == -1)
68                 {
69                         free(dev);
70                         exfat_error("failed to open `%s' in read-only mode", spec);
71                         return NULL;
72                 }
73                 dev->mode = EXFAT_MODE_RO;
74                 break;
75         case EXFAT_MODE_RW:
76                 dev->fd = open(spec, O_RDWR);
77                 if (dev->fd == -1)
78                 {
79                         free(dev);
80                         exfat_error("failed to open `%s' in read-write mode", spec);
81                         return NULL;
82                 }
83                 dev->mode = EXFAT_MODE_RW;
84                 break;
85         case EXFAT_MODE_ANY:
86                 dev->fd = open(spec, O_RDWR);
87                 if (dev->fd != -1)
88                 {
89                         dev->mode = EXFAT_MODE_RW;
90                         break;
91                 }
92                 dev->fd = open(spec, O_RDONLY);
93                 if (dev->fd != -1)
94                 {
95                         dev->mode = EXFAT_MODE_RO;
96                         exfat_warn("`%s' is write-protected, mounting read-only", spec);
97                         break;
98                 }
99                 free(dev);
100                 exfat_error("failed to open `%s'", spec);
101                 return NULL;
102         }
103
104         if (fstat(dev->fd, &stbuf) != 0)
105         {
106                 close(dev->fd);
107                 free(dev);
108                 exfat_error("failed to fstat `%s'", spec);
109                 return NULL;
110         }
111         if (!S_ISBLK(stbuf.st_mode) &&
112                 !S_ISCHR(stbuf.st_mode) &&
113                 !S_ISREG(stbuf.st_mode))
114         {
115                 close(dev->fd);
116                 free(dev);
117                 exfat_error("`%s' is neither a device, nor a regular file", spec);
118                 return NULL;
119         }
120
121 #ifdef USE_UBLIO
122         memset(&up, 0, sizeof(struct ublio_param));
123         up.up_blocksize = 256 * 1024;
124         up.up_items = 64;
125         up.up_grace = 32;
126         up.up_priv = &dev->fd;
127
128         dev->pos = 0;
129         dev->ufh = ublio_open(&up);
130         if (dev->ufh == NULL)
131         {
132                 close(dev->fd);
133                 free(dev);
134                 exfat_error("failed to initialize ublio");
135                 return NULL;
136         }
137 #endif
138
139         return dev;
140 }
141
142 int exfat_close(struct exfat_dev* dev)
143 {
144 #ifdef USE_UBLIO
145         if (ublio_close(dev->ufh) != 0)
146                 exfat_error("failed to close ublio");
147 #endif
148         if (close(dev->fd) != 0)
149         {
150                 free(dev);
151                 exfat_error("failed to close device");
152                 return 1;
153         }
154         free(dev);
155         return 0;
156 }
157
158 int exfat_fsync(struct exfat_dev* dev)
159 {
160 #ifdef USE_UBLIO
161         if (ublio_fsync(dev->ufh) != 0)
162 #else
163         if (fsync(dev->fd) != 0)
164 #endif
165         {
166                 exfat_error("fsync failed");
167                 return 1;
168         }
169         return 0;
170 }
171
172 enum exfat_mode exfat_mode(const struct exfat_dev* dev)
173 {
174         return dev->mode;
175 }
176
177 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
178 {
179 #ifdef USE_UBLIO
180         /* XXX SEEK_CUR will be handled incorrectly */
181         return dev->pos = lseek(dev->fd, offset, whence);
182 #else
183         return lseek(dev->fd, offset, whence);
184 #endif
185 }
186
187 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
188 {
189 #ifdef USE_UBLIO
190         ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
191         if (result >= 0)
192                 dev->pos += size;
193         return result;
194 #else
195         return read(dev->fd, buffer, size);
196 #endif
197 }
198
199 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
200 {
201 #ifdef USE_UBLIO
202         ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
203         if (result >= 0)
204                 dev->pos += size;
205         return result;
206 #else
207         return write(dev->fd, buffer, size);
208 #endif
209 }
210
211 void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
212                 off_t offset)
213 {
214 #ifdef USE_UBLIO
215         if (ublio_pread(dev->ufh, buffer, size, offset) != size)
216 #else
217         if (pread(dev->fd, buffer, size, offset) != size)
218 #endif
219                 exfat_bug("failed to read %zu bytes from file at %"PRIu64, size,
220                                 (uint64_t) offset);
221 }
222
223 void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
224                 off_t offset)
225 {
226 #ifdef USE_UBLIO
227         if (ublio_pwrite(dev->ufh, buffer, size, offset) != size)
228 #else
229         if (pwrite(dev->fd, buffer, size, offset) != size)
230 #endif
231                 exfat_bug("failed to write %zu bytes to file at %"PRIu64, size,
232                                 (uint64_t) offset);
233 }
234
235 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
236                 void* buffer, size_t size, off_t offset)
237 {
238         cluster_t cluster;
239         char* bufp = buffer;
240         off_t lsize, loffset, remainder;
241
242         if (offset >= node->size)
243                 return 0;
244         if (size == 0)
245                 return 0;
246
247         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
248         if (CLUSTER_INVALID(cluster))
249         {
250                 exfat_error("invalid cluster 0x%x while reading", cluster);
251                 return -1;
252         }
253
254         loffset = offset % CLUSTER_SIZE(*ef->sb);
255         remainder = MIN(size, node->size - offset);
256         while (remainder > 0)
257         {
258                 if (CLUSTER_INVALID(cluster))
259                 {
260                         exfat_error("invalid cluster 0x%x while reading", cluster);
261                         return -1;
262                 }
263                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
264                 exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
265                 bufp += lsize;
266                 loffset = 0;
267                 remainder -= lsize;
268                 cluster = exfat_next_cluster(ef, node, cluster);
269         }
270         if (!ef->ro && !ef->noatime)
271                 exfat_update_atime(node);
272         return size - remainder;
273 }
274
275 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
276                 const void* buffer, size_t size, off_t offset)
277 {
278         cluster_t cluster;
279         const char* bufp = buffer;
280         off_t lsize, loffset, remainder;
281
282         if (offset + size > node->size)
283         {
284                 int rc = exfat_truncate(ef, node, offset + size);
285                 if (rc != 0)
286                         return rc;
287         }
288         if (size == 0)
289                 return 0;
290
291         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
292         if (CLUSTER_INVALID(cluster))
293         {
294                 exfat_error("invalid cluster 0x%x while writing", cluster);
295                 return -1;
296         }
297
298         loffset = offset % CLUSTER_SIZE(*ef->sb);
299         remainder = size;
300         while (remainder > 0)
301         {
302                 if (CLUSTER_INVALID(cluster))
303                 {
304                         exfat_error("invalid cluster 0x%x while writing", cluster);
305                         return -1;
306                 }
307                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
308                 exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
309                 bufp += lsize;
310                 loffset = 0;
311                 remainder -= lsize;
312                 cluster = exfat_next_cluster(ef, node, cluster);
313         }
314         exfat_update_mtime(node);
315         return size - remainder;
316 }