OSDN Git Service

Handle I/O error in exfat_flush().
[android-x86/external-exfat.git] / libexfat / io.c
1 /*
2         io.c (02.09.09)
3         exFAT file system implementation library.
4
5         Free exFAT implementation.
6         Copyright (C) 2010-2013  Andrew Nayenko
7
8         This program is free software; you can redistribute it and/or modify
9         it under the terms of the GNU General Public License as published by
10         the Free Software Foundation, either version 2 of the License, or
11         (at your option) any later version.
12
13         This program is distributed in the hope that it will be useful,
14         but WITHOUT ANY WARRANTY; without even the implied warranty of
15         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16         GNU General Public License for more details.
17
18         You should have received a copy of the GNU General Public License along
19         with this program; if not, write to the Free Software Foundation, Inc.,
20         51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 */
22
23 #include "exfat.h"
24 #include <inttypes.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/mount.h>
28 #include <fcntl.h>
29 #include <unistd.h>
30 #include <string.h>
31 #ifdef __APPLE__
32 #include <sys/disk.h>
33 #endif
34 #ifdef USE_UBLIO
35 #include <sys/uio.h>
36 #include <ublio.h>
37 #endif
38
39 struct exfat_dev
40 {
41         int fd;
42         enum exfat_mode mode;
43         off_t size; /* in bytes */
44 #ifdef USE_UBLIO
45         off_t pos;
46         ublio_filehandle_t ufh;
47 #endif
48 };
49
50 static int open_ro(const char* spec)
51 {
52         return open(spec, O_RDONLY);
53 }
54
55 static int open_rw(const char* spec)
56 {
57         int fd = open(spec, O_RDWR);
58 #ifdef __linux__
59         int ro = 0;
60
61         /*
62            This ioctl is needed because after "blockdev --setro" kernel still
63            allows to open the device in read-write mode but fails writes.
64         */
65         if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
66         {
67                 close(fd);
68                 return -1;
69         }
70 #endif
71         return fd;
72 }
73
74 struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
75 {
76         struct exfat_dev* dev;
77         struct stat stbuf;
78 #ifdef USE_UBLIO
79         struct ublio_param up;
80 #endif
81
82         dev = malloc(sizeof(struct exfat_dev));
83         if (dev == NULL)
84         {
85                 exfat_error("failed to allocate memory for device structure");
86                 return NULL;
87         }
88
89         switch (mode)
90         {
91         case EXFAT_MODE_RO:
92                 dev->fd = open_ro(spec);
93                 if (dev->fd == -1)
94                 {
95                         free(dev);
96                         exfat_error("failed to open `%s' in read-only mode", spec);
97                         return NULL;
98                 }
99                 dev->mode = EXFAT_MODE_RO;
100                 break;
101         case EXFAT_MODE_RW:
102                 dev->fd = open_rw(spec);
103                 if (dev->fd == -1)
104                 {
105                         free(dev);
106                         exfat_error("failed to open `%s' in read-write mode", spec);
107                         return NULL;
108                 }
109                 dev->mode = EXFAT_MODE_RW;
110                 break;
111         case EXFAT_MODE_ANY:
112                 dev->fd = open_rw(spec);
113                 if (dev->fd != -1)
114                 {
115                         dev->mode = EXFAT_MODE_RW;
116                         break;
117                 }
118                 dev->fd = open_ro(spec);
119                 if (dev->fd != -1)
120                 {
121                         dev->mode = EXFAT_MODE_RO;
122                         exfat_warn("`%s' is write-protected, mounting read-only", spec);
123                         break;
124                 }
125                 free(dev);
126                 exfat_error("failed to open `%s'", spec);
127                 return NULL;
128         }
129
130         if (fstat(dev->fd, &stbuf) != 0)
131         {
132                 close(dev->fd);
133                 free(dev);
134                 exfat_error("failed to fstat `%s'", spec);
135                 return NULL;
136         }
137         if (!S_ISBLK(stbuf.st_mode) &&
138                 !S_ISCHR(stbuf.st_mode) &&
139                 !S_ISREG(stbuf.st_mode))
140         {
141                 close(dev->fd);
142                 free(dev);
143                 exfat_error("`%s' is neither a device, nor a regular file", spec);
144                 return NULL;
145         }
146
147 #ifdef __APPLE__
148         if (!S_ISREG(stbuf.st_mode))
149         {
150                 uint32_t block_size = 0;
151                 uint64_t blocks = 0;
152
153                 if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
154                 {
155                         close(dev->fd);
156                         free(dev);
157                         exfat_error("failed to get block size");
158                         return NULL;
159                 }
160                 if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
161                 {
162                         close(dev->fd);
163                         free(dev);
164                         exfat_error("failed to get blocks count");
165                         return NULL;
166                 }
167                 dev->size = blocks * block_size;
168         }
169         else
170 #endif
171         {
172                 /* works for Linux, FreeBSD, Solaris */
173                 dev->size = exfat_seek(dev, 0, SEEK_END);
174                 if (dev->size <= 0)
175                 {
176                         close(dev->fd);
177                         free(dev);
178                         exfat_error("failed to get size of `%s'", spec);
179                         return NULL;
180                 }
181                 if (exfat_seek(dev, 0, SEEK_SET) == -1)
182                 {
183                         close(dev->fd);
184                         free(dev);
185                         exfat_error("failed to seek to the beginning of `%s'", spec);
186                         return NULL;
187                 }
188         }
189
190 #ifdef USE_UBLIO
191         memset(&up, 0, sizeof(struct ublio_param));
192         up.up_blocksize = 256 * 1024;
193         up.up_items = 64;
194         up.up_grace = 32;
195         up.up_priv = &dev->fd;
196
197         dev->pos = 0;
198         dev->ufh = ublio_open(&up);
199         if (dev->ufh == NULL)
200         {
201                 close(dev->fd);
202                 free(dev);
203                 exfat_error("failed to initialize ublio");
204                 return NULL;
205         }
206 #endif
207
208         return dev;
209 }
210
211 int exfat_close(struct exfat_dev* dev)
212 {
213 #ifdef USE_UBLIO
214         if (ublio_close(dev->ufh) != 0)
215                 exfat_error("failed to close ublio");
216 #endif
217         if (close(dev->fd) != 0)
218         {
219                 free(dev);
220                 exfat_error("failed to close device");
221                 return 1;
222         }
223         free(dev);
224         return 0;
225 }
226
227 int exfat_fsync(struct exfat_dev* dev)
228 {
229 #ifdef USE_UBLIO
230         if (ublio_fsync(dev->ufh) != 0)
231 #else
232         if (fsync(dev->fd) != 0)
233 #endif
234         {
235                 exfat_error("fsync failed");
236                 return 1;
237         }
238         return 0;
239 }
240
241 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
242 {
243         return dev->mode;
244 }
245
246 off_t exfat_get_size(const struct exfat_dev* dev)
247 {
248         return dev->size;
249 }
250
251 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
252 {
253 #ifdef USE_UBLIO
254         /* XXX SEEK_CUR will be handled incorrectly */
255         return dev->pos = lseek(dev->fd, offset, whence);
256 #else
257         return lseek(dev->fd, offset, whence);
258 #endif
259 }
260
261 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
262 {
263 #ifdef USE_UBLIO
264         ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
265         if (result >= 0)
266                 dev->pos += size;
267         return result;
268 #else
269         return read(dev->fd, buffer, size);
270 #endif
271 }
272
273 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
274 {
275 #ifdef USE_UBLIO
276         ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
277         if (result >= 0)
278                 dev->pos += size;
279         return result;
280 #else
281         return write(dev->fd, buffer, size);
282 #endif
283 }
284
285 ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
286                 off_t offset)
287 {
288 #ifdef USE_UBLIO
289         return ublio_pread(dev->ufh, buffer, size, offset);
290 #else
291         return pread(dev->fd, buffer, size, offset);
292 #endif
293 }
294
295 ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
296                 off_t offset)
297 {
298 #ifdef USE_UBLIO
299         return ublio_pwrite(dev->ufh, buffer, size, offset);
300 #else
301         return pwrite(dev->fd, buffer, size, offset);
302 #endif
303 }
304
305 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
306                 void* buffer, size_t size, off_t offset)
307 {
308         cluster_t cluster;
309         char* bufp = buffer;
310         off_t lsize, loffset, remainder;
311
312         if (offset >= node->size)
313                 return 0;
314         if (size == 0)
315                 return 0;
316
317         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
318         if (CLUSTER_INVALID(cluster))
319         {
320                 exfat_error("invalid cluster 0x%x while reading", cluster);
321                 return -1;
322         }
323
324         loffset = offset % CLUSTER_SIZE(*ef->sb);
325         remainder = MIN(size, node->size - offset);
326         while (remainder > 0)
327         {
328                 if (CLUSTER_INVALID(cluster))
329                 {
330                         exfat_error("invalid cluster 0x%x while reading", cluster);
331                         return -1;
332                 }
333                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
334                 if (exfat_pread(ef->dev, bufp, lsize,
335                                         exfat_c2o(ef, cluster) + loffset) < 0)
336                 {
337                         exfat_error("failed to read cluster %#x", cluster);
338                         return -1;
339                 }
340                 bufp += lsize;
341                 loffset = 0;
342                 remainder -= lsize;
343                 cluster = exfat_next_cluster(ef, node, cluster);
344         }
345         if (!ef->ro && !ef->noatime)
346                 exfat_update_atime(node);
347         return MIN(size, node->size - offset) - remainder;
348 }
349
350 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
351                 const void* buffer, size_t size, off_t offset)
352 {
353         cluster_t cluster;
354         const char* bufp = buffer;
355         off_t lsize, loffset, remainder;
356
357         if (offset > node->size)
358                 if (exfat_truncate(ef, node, offset, true) != 0)
359                         return -1;
360         if (offset + size > node->size)
361                 if (exfat_truncate(ef, node, offset + size, false) != 0)
362                         return -1;
363         if (size == 0)
364                 return 0;
365
366         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
367         if (CLUSTER_INVALID(cluster))
368         {
369                 exfat_error("invalid cluster 0x%x while writing", cluster);
370                 return -1;
371         }
372
373         loffset = offset % CLUSTER_SIZE(*ef->sb);
374         remainder = size;
375         while (remainder > 0)
376         {
377                 if (CLUSTER_INVALID(cluster))
378                 {
379                         exfat_error("invalid cluster 0x%x while writing", cluster);
380                         return -1;
381                 }
382                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
383                 if (exfat_pwrite(ef->dev, bufp, lsize,
384                                 exfat_c2o(ef, cluster) + loffset) < 0)
385                 {
386                         exfat_error("failed to write cluster %#x", cluster);
387                         return -1;
388                 }
389                 bufp += lsize;
390                 loffset = 0;
391                 remainder -= lsize;
392                 cluster = exfat_next_cluster(ef, node, cluster);
393         }
394         exfat_update_mtime(node);
395         return size - remainder;
396 }