OSDN Git Service

Fixed return codes from exfat_close() and exfat_fsync(): return -EIO on error instead...
[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 #include <errno.h>
32 #ifdef __APPLE__
33 #include <sys/disk.h>
34 #endif
35 #ifdef USE_UBLIO
36 #include <sys/uio.h>
37 #include <ublio.h>
38 #endif
39
40 struct exfat_dev
41 {
42         int fd;
43         enum exfat_mode mode;
44         off_t size; /* in bytes */
45 #ifdef USE_UBLIO
46         off_t pos;
47         ublio_filehandle_t ufh;
48 #endif
49 };
50
51 static int open_ro(const char* spec)
52 {
53         return open(spec, O_RDONLY);
54 }
55
56 static int open_rw(const char* spec)
57 {
58         int fd = open(spec, O_RDWR);
59 #ifdef __linux__
60         int ro = 0;
61
62         /*
63            This ioctl is needed because after "blockdev --setro" kernel still
64            allows to open the device in read-write mode but fails writes.
65         */
66         if (fd != -1 && ioctl(fd, BLKROGET, &ro) == 0 && ro)
67         {
68                 close(fd);
69                 return -1;
70         }
71 #endif
72         return fd;
73 }
74
75 struct exfat_dev* exfat_open(const char* spec, enum exfat_mode mode)
76 {
77         struct exfat_dev* dev;
78         struct stat stbuf;
79 #ifdef USE_UBLIO
80         struct ublio_param up;
81 #endif
82
83         dev = malloc(sizeof(struct exfat_dev));
84         if (dev == NULL)
85         {
86                 exfat_error("failed to allocate memory for device structure");
87                 return NULL;
88         }
89
90         switch (mode)
91         {
92         case EXFAT_MODE_RO:
93                 dev->fd = open_ro(spec);
94                 if (dev->fd == -1)
95                 {
96                         free(dev);
97                         exfat_error("failed to open `%s' in read-only mode", spec);
98                         return NULL;
99                 }
100                 dev->mode = EXFAT_MODE_RO;
101                 break;
102         case EXFAT_MODE_RW:
103                 dev->fd = open_rw(spec);
104                 if (dev->fd == -1)
105                 {
106                         free(dev);
107                         exfat_error("failed to open `%s' in read-write mode", spec);
108                         return NULL;
109                 }
110                 dev->mode = EXFAT_MODE_RW;
111                 break;
112         case EXFAT_MODE_ANY:
113                 dev->fd = open_rw(spec);
114                 if (dev->fd != -1)
115                 {
116                         dev->mode = EXFAT_MODE_RW;
117                         break;
118                 }
119                 dev->fd = open_ro(spec);
120                 if (dev->fd != -1)
121                 {
122                         dev->mode = EXFAT_MODE_RO;
123                         exfat_warn("`%s' is write-protected, mounting read-only", spec);
124                         break;
125                 }
126                 free(dev);
127                 exfat_error("failed to open `%s'", spec);
128                 return NULL;
129         }
130
131         if (fstat(dev->fd, &stbuf) != 0)
132         {
133                 close(dev->fd);
134                 free(dev);
135                 exfat_error("failed to fstat `%s'", spec);
136                 return NULL;
137         }
138         if (!S_ISBLK(stbuf.st_mode) &&
139                 !S_ISCHR(stbuf.st_mode) &&
140                 !S_ISREG(stbuf.st_mode))
141         {
142                 close(dev->fd);
143                 free(dev);
144                 exfat_error("`%s' is neither a device, nor a regular file", spec);
145                 return NULL;
146         }
147
148 #ifdef __APPLE__
149         if (!S_ISREG(stbuf.st_mode))
150         {
151                 uint32_t block_size = 0;
152                 uint64_t blocks = 0;
153
154                 if (ioctl(dev->fd, DKIOCGETBLOCKSIZE, &block_size) != 0)
155                 {
156                         close(dev->fd);
157                         free(dev);
158                         exfat_error("failed to get block size");
159                         return NULL;
160                 }
161                 if (ioctl(dev->fd, DKIOCGETBLOCKCOUNT, &blocks) != 0)
162                 {
163                         close(dev->fd);
164                         free(dev);
165                         exfat_error("failed to get blocks count");
166                         return NULL;
167                 }
168                 dev->size = blocks * block_size;
169         }
170         else
171 #endif
172         {
173                 /* works for Linux, FreeBSD, Solaris */
174                 dev->size = exfat_seek(dev, 0, SEEK_END);
175                 if (dev->size <= 0)
176                 {
177                         close(dev->fd);
178                         free(dev);
179                         exfat_error("failed to get size of `%s'", spec);
180                         return NULL;
181                 }
182                 if (exfat_seek(dev, 0, SEEK_SET) == -1)
183                 {
184                         close(dev->fd);
185                         free(dev);
186                         exfat_error("failed to seek to the beginning of `%s'", spec);
187                         return NULL;
188                 }
189         }
190
191 #ifdef USE_UBLIO
192         memset(&up, 0, sizeof(struct ublio_param));
193         up.up_blocksize = 256 * 1024;
194         up.up_items = 64;
195         up.up_grace = 32;
196         up.up_priv = &dev->fd;
197
198         dev->pos = 0;
199         dev->ufh = ublio_open(&up);
200         if (dev->ufh == NULL)
201         {
202                 close(dev->fd);
203                 free(dev);
204                 exfat_error("failed to initialize ublio");
205                 return NULL;
206         }
207 #endif
208
209         return dev;
210 }
211
212 int exfat_close(struct exfat_dev* dev)
213 {
214         int rc = 0;
215
216 #ifdef USE_UBLIO
217         if (ublio_close(dev->ufh) != 0)
218         {
219                 exfat_error("failed to close ublio");
220                 rc = -EIO;
221         }
222 #endif
223         if (close(dev->fd) != 0)
224         {
225                 exfat_error("failed to close device");
226                 rc = -EIO;
227         }
228         free(dev);
229         return rc;
230 }
231
232 int exfat_fsync(struct exfat_dev* dev)
233 {
234         int rc = 0;
235
236 #ifdef USE_UBLIO
237         if (ublio_fsync(dev->ufh) != 0)
238         {
239                 exfat_error("ublio fsync failed");
240                 rc = -EIO;
241         }
242 #endif
243         if (fsync(dev->fd) != 0)
244         {
245                 exfat_error("fsync failed");
246                 rc = -EIO;
247         }
248         return rc;
249 }
250
251 enum exfat_mode exfat_get_mode(const struct exfat_dev* dev)
252 {
253         return dev->mode;
254 }
255
256 off_t exfat_get_size(const struct exfat_dev* dev)
257 {
258         return dev->size;
259 }
260
261 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
262 {
263 #ifdef USE_UBLIO
264         /* XXX SEEK_CUR will be handled incorrectly */
265         return dev->pos = lseek(dev->fd, offset, whence);
266 #else
267         return lseek(dev->fd, offset, whence);
268 #endif
269 }
270
271 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
272 {
273 #ifdef USE_UBLIO
274         ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
275         if (result >= 0)
276                 dev->pos += size;
277         return result;
278 #else
279         return read(dev->fd, buffer, size);
280 #endif
281 }
282
283 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
284 {
285 #ifdef USE_UBLIO
286         ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
287         if (result >= 0)
288                 dev->pos += size;
289         return result;
290 #else
291         return write(dev->fd, buffer, size);
292 #endif
293 }
294
295 ssize_t exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
296                 off_t offset)
297 {
298 #ifdef USE_UBLIO
299         return ublio_pread(dev->ufh, buffer, size, offset);
300 #else
301         return pread(dev->fd, buffer, size, offset);
302 #endif
303 }
304
305 ssize_t exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
306                 off_t offset)
307 {
308 #ifdef USE_UBLIO
309         return ublio_pwrite(dev->ufh, buffer, size, offset);
310 #else
311         return pwrite(dev->fd, buffer, size, offset);
312 #endif
313 }
314
315 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
316                 void* buffer, size_t size, off_t offset)
317 {
318         cluster_t cluster;
319         char* bufp = buffer;
320         off_t lsize, loffset, remainder;
321
322         if (offset >= node->size)
323                 return 0;
324         if (size == 0)
325                 return 0;
326
327         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
328         if (CLUSTER_INVALID(cluster))
329         {
330                 exfat_error("invalid cluster 0x%x while reading", cluster);
331                 return -1;
332         }
333
334         loffset = offset % CLUSTER_SIZE(*ef->sb);
335         remainder = MIN(size, node->size - offset);
336         while (remainder > 0)
337         {
338                 if (CLUSTER_INVALID(cluster))
339                 {
340                         exfat_error("invalid cluster 0x%x while reading", cluster);
341                         return -1;
342                 }
343                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
344                 if (exfat_pread(ef->dev, bufp, lsize,
345                                         exfat_c2o(ef, cluster) + loffset) < 0)
346                 {
347                         exfat_error("failed to read cluster %#x", cluster);
348                         return -1;
349                 }
350                 bufp += lsize;
351                 loffset = 0;
352                 remainder -= lsize;
353                 cluster = exfat_next_cluster(ef, node, cluster);
354         }
355         if (!ef->ro && !ef->noatime)
356                 exfat_update_atime(node);
357         return MIN(size, node->size - offset) - remainder;
358 }
359
360 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
361                 const void* buffer, size_t size, off_t offset)
362 {
363         cluster_t cluster;
364         const char* bufp = buffer;
365         off_t lsize, loffset, remainder;
366
367         if (offset > node->size)
368                 if (exfat_truncate(ef, node, offset, true) != 0)
369                         return -1;
370         if (offset + size > node->size)
371                 if (exfat_truncate(ef, node, offset + size, false) != 0)
372                         return -1;
373         if (size == 0)
374                 return 0;
375
376         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
377         if (CLUSTER_INVALID(cluster))
378         {
379                 exfat_error("invalid cluster 0x%x while writing", cluster);
380                 return -1;
381         }
382
383         loffset = offset % CLUSTER_SIZE(*ef->sb);
384         remainder = size;
385         while (remainder > 0)
386         {
387                 if (CLUSTER_INVALID(cluster))
388                 {
389                         exfat_error("invalid cluster 0x%x while writing", cluster);
390                         return -1;
391                 }
392                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
393                 if (exfat_pwrite(ef->dev, bufp, lsize,
394                                 exfat_c2o(ef, cluster) + loffset) < 0)
395                 {
396                         exfat_error("failed to write cluster %#x", cluster);
397                         return -1;
398                 }
399                 bufp += lsize;
400                 loffset = 0;
401                 remainder -= lsize;
402                 cluster = exfat_next_cluster(ef, node, cluster);
403         }
404         exfat_update_mtime(node);
405         return size - remainder;
406 }