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