OSDN Git Service

Add ublio support for FreeBSD.
[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) 2009, 2010  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 #define _XOPEN_SOURCE 500 /* for pread() and pwrite() in Linux */
22 #include "exfat.h"
23 #include <inttypes.h>
24 #include <sys/types.h>
25 #include <sys/uio.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <string.h>
30 #ifdef USE_UBLIO
31 #include <sys/uio.h>
32 #include <ublio.h>
33 #endif
34
35 #if _FILE_OFFSET_BITS != 64
36         #error You should define _FILE_OFFSET_BITS=64
37 #endif
38
39 struct exfat_dev
40 {
41         int fd;
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, int ro)
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         dev->fd = open(spec, ro ? O_RDONLY : O_RDWR);
64         if (dev->fd < 0)
65         {
66                 free(dev);
67                 exfat_error("failed to open `%s' in read-%s mode", spec,
68                                 ro ? "only" : "write");
69                 return NULL;
70         }
71         if (fstat(dev->fd, &stbuf) != 0)
72         {
73                 close(dev->fd);
74                 free(dev);
75                 exfat_error("failed to fstat `%s'", spec);
76                 return NULL;
77         }
78         if (!S_ISBLK(stbuf.st_mode) &&
79                 !S_ISCHR(stbuf.st_mode) &&
80                 !S_ISREG(stbuf.st_mode))
81         {
82                 close(dev->fd);
83                 free(dev);
84                 exfat_error("`%s' is neither a block device, nor a regular file",
85                                 spec);
86                 return NULL;
87         }
88
89 #ifdef USE_UBLIO
90         memset(&up, 0, sizeof(struct ublio_param));
91         up.up_blocksize = 256 * 1024;
92         up.up_items = 64;
93         up.up_grace = 32;
94         up.up_priv = &dev->fd;
95
96         dev->pos = 0;
97         dev->ufh = ublio_open(&up);
98         if (dev->ufh == NULL)
99         {
100                 close(dev->fd);
101                 free(dev);
102                 exfat_error("failed to initialize ublio");
103                 return NULL;
104         }
105 #endif
106
107         return dev;
108 }
109
110 int exfat_close(struct exfat_dev* dev)
111 {
112 #ifdef USE_UBLIO
113         if (ublio_close(dev->ufh) != 0)
114                 exfat_error("failed to close ublio");
115 #endif
116         if (close(dev->fd) != 0)
117         {
118                 free(dev);
119                 exfat_error("failed to close device");
120                 return 1;
121         }
122         free(dev);
123         return 0;
124 }
125
126 int exfat_fsync(struct exfat_dev* dev)
127 {
128 #ifdef USE_UBLIO
129         if (ublio_fsync(dev->ufh) != 0)
130 #else
131         if (fsync(dev->fd) != 0)
132 #endif
133         {
134                 exfat_error("fsync failed");
135                 return 1;
136         }
137         return 0;
138 }
139
140 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
141 {
142 #ifdef USE_UBLIO
143         /* XXX SEEK_CUR will be handled incorrectly */
144         return dev->pos = lseek(dev->fd, offset, whence);
145 #else
146         return lseek(dev->fd, offset, whence);
147 #endif
148 }
149
150 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
151 {
152 #ifdef USE_UBLIO
153         ssize_t result = ublio_pread(dev->ufh, buffer, size, dev->pos);
154         if (result >= 0)
155                 dev->pos += size;
156         return result;
157 #else
158         return read(dev->fd, buffer, size);
159 #endif
160 }
161
162 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
163 {
164 #ifdef USE_UBLIO
165         ssize_t result = ublio_pwrite(dev->ufh, buffer, size, dev->pos);
166         if (result >= 0)
167                 dev->pos += size;
168         return result;
169 #else
170         return write(dev->fd, buffer, size);
171 #endif
172 }
173
174 void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
175                 off_t offset)
176 {
177 #ifdef USE_UBLIO
178         if (ublio_pread(dev->ufh, buffer, size, offset) != size)
179 #else
180         if (pread(dev->fd, buffer, size, offset) != size)
181 #endif
182                 exfat_bug("failed to read %zu bytes from file at %"PRIu64, size,
183                                 (uint64_t) offset);
184 }
185
186 void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
187                 off_t offset)
188 {
189 #ifdef USE_UBLIO
190         if (ublio_pwrite(dev->ufh, buffer, size, offset) != size)
191 #else
192         if (pwrite(dev->fd, buffer, size, offset) != size)
193 #endif
194                 exfat_bug("failed to write %zu bytes to file at %"PRIu64, size,
195                                 (uint64_t) offset);
196 }
197
198 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
199                 void* buffer, size_t size, off_t offset)
200 {
201         cluster_t cluster;
202         char* bufp = buffer;
203         off_t lsize, loffset, remainder;
204
205         if (offset >= node->size)
206                 return 0;
207         if (size == 0)
208                 return 0;
209
210         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
211         if (CLUSTER_INVALID(cluster))
212         {
213                 exfat_error("got invalid cluster");
214                 return -1;
215         }
216
217         loffset = offset % CLUSTER_SIZE(*ef->sb);
218         remainder = MIN(size, node->size - offset);
219         while (remainder > 0)
220         {
221                 if (CLUSTER_INVALID(cluster))
222                 {
223                         exfat_error("got invalid cluster");
224                         return -1;
225                 }
226                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
227                 exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
228                 bufp += lsize;
229                 loffset = 0;
230                 remainder -= lsize;
231                 cluster = exfat_next_cluster(ef, node, cluster);
232         }
233         if (!ef->ro && !ef->noatime)
234                 exfat_update_atime(node);
235         return size - remainder;
236 }
237
238 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
239                 const void* buffer, size_t size, off_t offset)
240 {
241         cluster_t cluster;
242         const char* bufp = buffer;
243         off_t lsize, loffset, remainder;
244
245         if (offset + size > node->size)
246         {
247                 int rc = exfat_truncate(ef, node, offset + size);
248                 if (rc != 0)
249                         return rc;
250         }
251         if (size == 0)
252                 return 0;
253
254         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
255         if (CLUSTER_INVALID(cluster))
256         {
257                 exfat_error("got invalid cluster");
258                 return -1;
259         }
260
261         loffset = offset % CLUSTER_SIZE(*ef->sb);
262         remainder = size;
263         while (remainder > 0)
264         {
265                 if (CLUSTER_INVALID(cluster))
266                 {
267                         exfat_error("got invalid cluster");
268                         return -1;
269                 }
270                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
271                 exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
272                 bufp += lsize;
273                 loffset = 0;
274                 remainder -= lsize;
275                 cluster = exfat_next_cluster(ef, node, cluster);
276         }
277         exfat_update_mtime(node);
278         return size - remainder;
279 }