OSDN Git Service

Removed extra include.
[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 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 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 USE_UBLIO
146         memset(&up, 0, sizeof(struct ublio_param));
147         up.up_blocksize = 256 * 1024;
148         up.up_items = 64;
149         up.up_grace = 32;
150         up.up_priv = &dev->fd;
151
152         dev->pos = 0;
153         dev->ufh = ublio_open(&up);
154         if (dev->ufh == NULL)
155         {
156                 close(dev->fd);
157                 free(dev);
158                 exfat_error("failed to initialize ublio");
159                 return NULL;
160         }
161 #endif
162
163         return dev;
164 }
165
166 int exfat_close(struct exfat_dev* dev)
167 {
168 #ifdef USE_UBLIO
169         if (ublio_close(dev->ufh) != 0)
170                 exfat_error("failed to close ublio");
171 #endif
172         if (close(dev->fd) != 0)
173         {
174                 free(dev);
175                 exfat_error("failed to close device");
176                 return 1;
177         }
178         free(dev);
179         return 0;
180 }
181
182 int exfat_fsync(struct exfat_dev* dev)
183 {
184 #ifdef USE_UBLIO
185         if (ublio_fsync(dev->ufh) != 0)
186 #else
187         if (fsync(dev->fd) != 0)
188 #endif
189         {
190                 exfat_error("fsync failed");
191                 return 1;
192         }
193         return 0;
194 }
195
196 enum exfat_mode exfat_mode(const struct exfat_dev* dev)
197 {
198         return dev->mode;
199 }
200
201 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
202 {
203 #ifdef USE_UBLIO
204         /* XXX SEEK_CUR will be handled incorrectly */
205         return dev->pos = lseek(dev->fd, offset, whence);
206 #else
207         return lseek(dev->fd, offset, whence);
208 #endif
209 }
210
211 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
212 {
213 #ifdef USE_UBLIO
214         ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
215         if (result >= 0)
216                 dev->pos += size;
217         return result;
218 #else
219         return read(dev->fd, buffer, size);
220 #endif
221 }
222
223 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
224 {
225 #ifdef USE_UBLIO
226         ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
227         if (result >= 0)
228                 dev->pos += size;
229         return result;
230 #else
231         return write(dev->fd, buffer, size);
232 #endif
233 }
234
235 void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
236                 off_t offset)
237 {
238 #ifdef USE_UBLIO
239         if (ublio_pread(dev->ufh, buffer, size, offset) != size)
240 #else
241         if (pread(dev->fd, buffer, size, offset) != size)
242 #endif
243                 exfat_bug("failed to read %zu bytes from file at %"PRIu64, size,
244                                 (uint64_t) offset);
245 }
246
247 void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
248                 off_t offset)
249 {
250 #ifdef USE_UBLIO
251         if (ublio_pwrite(dev->ufh, buffer, size, offset) != size)
252 #else
253         if (pwrite(dev->fd, buffer, size, offset) != size)
254 #endif
255                 exfat_bug("failed to write %zu bytes to file at %"PRIu64, size,
256                                 (uint64_t) offset);
257 }
258
259 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
260                 void* buffer, size_t size, off_t offset)
261 {
262         cluster_t cluster;
263         char* bufp = buffer;
264         off_t lsize, loffset, remainder;
265
266         if (offset >= node->size)
267                 return 0;
268         if (size == 0)
269                 return 0;
270
271         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
272         if (CLUSTER_INVALID(cluster))
273         {
274                 exfat_error("invalid cluster 0x%x while reading", cluster);
275                 return -1;
276         }
277
278         loffset = offset % CLUSTER_SIZE(*ef->sb);
279         remainder = MIN(size, node->size - offset);
280         while (remainder > 0)
281         {
282                 if (CLUSTER_INVALID(cluster))
283                 {
284                         exfat_error("invalid cluster 0x%x while reading", cluster);
285                         return -1;
286                 }
287                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
288                 exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
289                 bufp += lsize;
290                 loffset = 0;
291                 remainder -= lsize;
292                 cluster = exfat_next_cluster(ef, node, cluster);
293         }
294         if (!ef->ro && !ef->noatime)
295                 exfat_update_atime(node);
296         return size - remainder;
297 }
298
299 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
300                 const void* buffer, size_t size, off_t offset)
301 {
302         cluster_t cluster;
303         const char* bufp = buffer;
304         off_t lsize, loffset, remainder;
305
306         if (offset + size > node->size)
307         {
308                 int rc = exfat_truncate(ef, node, offset + size);
309                 if (rc != 0)
310                         return rc;
311         }
312         if (size == 0)
313                 return 0;
314
315         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
316         if (CLUSTER_INVALID(cluster))
317         {
318                 exfat_error("invalid cluster 0x%x while writing", cluster);
319                 return -1;
320         }
321
322         loffset = offset % CLUSTER_SIZE(*ef->sb);
323         remainder = size;
324         while (remainder > 0)
325         {
326                 if (CLUSTER_INVALID(cluster))
327                 {
328                         exfat_error("invalid cluster 0x%x while writing", cluster);
329                         return -1;
330                 }
331                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
332                 exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
333                 bufp += lsize;
334                 loffset = 0;
335                 remainder -= lsize;
336                 cluster = exfat_next_cluster(ef, node, cluster);
337         }
338         exfat_update_mtime(node);
339         return size - remainder;
340 }