OSDN Git Service

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