OSDN Git Service

Represent device as an extensible structure instead of a plain Unix descriptor.
[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
30 #if _FILE_OFFSET_BITS != 64
31         #error You should define _FILE_OFFSET_BITS=64
32 #endif
33
34 struct exfat_dev
35 {
36         int fd;
37 };
38
39 struct exfat_dev* exfat_open(const char* spec, int ro)
40 {
41         struct exfat_dev* dev;
42         struct stat stbuf;
43
44         dev = malloc(sizeof(struct exfat_dev));
45         if (dev == NULL)
46         {
47                 exfat_error("failed to allocate memory for device structure");
48                 return NULL;
49         }
50
51         dev->fd = open(spec, ro ? O_RDONLY : O_RDWR);
52         if (dev->fd < 0)
53         {
54                 free(dev);
55                 exfat_error("failed to open `%s' in read-%s mode", spec,
56                                 ro ? "only" : "write");
57                 return NULL;
58         }
59         if (fstat(dev->fd, &stbuf) != 0)
60         {
61                 close(dev->fd);
62                 free(dev);
63                 exfat_error("failed to fstat `%s'", spec);
64                 return NULL;
65         }
66         if (!S_ISBLK(stbuf.st_mode) && !S_ISREG(stbuf.st_mode))
67         {
68                 close(dev->fd);
69                 free(dev);
70                 exfat_error("`%s' is neither a block device, nor a regular file",
71                                 spec);
72                 return NULL;
73         }
74         return dev;
75 }
76
77 int exfat_close(struct exfat_dev* dev)
78 {
79         if (close(dev->fd) != 0)
80         {
81                 free(dev);
82                 exfat_error("close failed");
83                 return 1;
84         }
85         free(dev);
86         return 0;
87 }
88
89 int exfat_fsync(struct exfat_dev* dev)
90 {
91         if (fsync(dev->fd) != 0)
92         {
93                 exfat_error("fsync failed");
94                 return 1;
95         }
96         return 0;
97 }
98
99 off_t exfat_seek(struct exfat_dev* dev, off_t offset, int whence)
100 {
101         return lseek(dev->fd, offset, whence);
102 }
103
104 ssize_t exfat_read(struct exfat_dev* dev, void* buffer, size_t size)
105 {
106         return read(dev->fd, buffer, size);
107 }
108
109 ssize_t exfat_write(struct exfat_dev* dev, const void* buffer, size_t size)
110 {
111         return write(dev->fd, buffer, size);
112 }
113
114 void exfat_pread(struct exfat_dev* dev, void* buffer, size_t size,
115                 off_t offset)
116 {
117         if (pread(dev->fd, buffer, size, offset) != size)
118                 exfat_bug("failed to read %zu bytes from file at %"PRIu64, size,
119                                 (uint64_t) offset);
120 }
121
122 void exfat_pwrite(struct exfat_dev* dev, const void* buffer, size_t size,
123                 off_t offset)
124 {
125         if (pwrite(dev->fd, buffer, size, offset) != size)
126                 exfat_bug("failed to write %zu bytes to file at %"PRIu64, size,
127                                 (uint64_t) offset);
128 }
129
130 ssize_t exfat_generic_pread(const struct exfat* ef, struct exfat_node* node,
131                 void* buffer, size_t size, off_t offset)
132 {
133         cluster_t cluster;
134         char* bufp = buffer;
135         off_t lsize, loffset, remainder;
136
137         if (offset >= node->size)
138                 return 0;
139         if (size == 0)
140                 return 0;
141
142         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
143         if (CLUSTER_INVALID(cluster))
144         {
145                 exfat_error("got invalid cluster");
146                 return -1;
147         }
148
149         loffset = offset % CLUSTER_SIZE(*ef->sb);
150         remainder = MIN(size, node->size - offset);
151         while (remainder > 0)
152         {
153                 if (CLUSTER_INVALID(cluster))
154                 {
155                         exfat_error("got invalid cluster");
156                         return -1;
157                 }
158                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
159                 exfat_pread(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
160                 bufp += lsize;
161                 loffset = 0;
162                 remainder -= lsize;
163                 cluster = exfat_next_cluster(ef, node, cluster);
164         }
165         if (!ef->ro && !ef->noatime)
166                 exfat_update_atime(node);
167         return size - remainder;
168 }
169
170 ssize_t exfat_generic_pwrite(struct exfat* ef, struct exfat_node* node,
171                 const void* buffer, size_t size, off_t offset)
172 {
173         cluster_t cluster;
174         const char* bufp = buffer;
175         off_t lsize, loffset, remainder;
176
177         if (offset + size > node->size)
178         {
179                 int rc = exfat_truncate(ef, node, offset + size);
180                 if (rc != 0)
181                         return rc;
182         }
183         if (size == 0)
184                 return 0;
185
186         cluster = exfat_advance_cluster(ef, node, offset / CLUSTER_SIZE(*ef->sb));
187         if (CLUSTER_INVALID(cluster))
188         {
189                 exfat_error("got invalid cluster");
190                 return -1;
191         }
192
193         loffset = offset % CLUSTER_SIZE(*ef->sb);
194         remainder = size;
195         while (remainder > 0)
196         {
197                 if (CLUSTER_INVALID(cluster))
198                 {
199                         exfat_error("got invalid cluster");
200                         return -1;
201                 }
202                 lsize = MIN(CLUSTER_SIZE(*ef->sb) - loffset, remainder);
203                 exfat_pwrite(ef->dev, bufp, lsize, exfat_c2o(ef, cluster) + loffset);
204                 bufp += lsize;
205                 loffset = 0;
206                 remainder -= lsize;
207                 cluster = exfat_next_cluster(ef, node, cluster);
208         }
209         exfat_update_mtime(node);
210         return size - remainder;
211 }