OSDN Git Service

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