1 // SPDX-License-Identifier: GPL-2.0+ OR Apache-2.0
2 /*
3 * Copyright (C) 2018 HUAWEI, Inc.
4 * http://www.huawei.com/
5 * Created by Li Guifu <bluce.liguifu@huawei.com>
6 */
7 #ifndef _LARGEFILE64_SOURCE
8 #define _LARGEFILE64_SOURCE
9 #endif
10 #ifndef _GNU_SOURCE
11 #define _GNU_SOURCE
12 #endif
13 #include <sys/stat.h>
14 #include <sys/ioctl.h>
15 #include "erofs/io.h"
16 #ifdef HAVE_LINUX_FS_H
17 #include <linux/fs.h>
18 #endif
19 #ifdef HAVE_LINUX_FALLOC_H
20 #include <linux/falloc.h>
21 #endif
22
23 #define EROFS_MODNAME "erofs_io"
24 #include "erofs/print.h"
25
26 static const char *erofs_devname;
27 int erofs_devfd = -1;
28 static u64 erofs_devsz;
29 static unsigned int erofs_nblobs, erofs_blobfd[256];
30
dev_get_blkdev_size(int fd,u64 * bytes)31 int dev_get_blkdev_size(int fd, u64 *bytes)
32 {
33 errno = ENOTSUP;
34 #ifdef BLKGETSIZE64
35 if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
36 return 0;
37 #endif
38
39 #ifdef BLKGETSIZE
40 {
41 unsigned long size;
42 if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
43 *bytes = ((u64)size << 9);
44 return 0;
45 }
46 }
47 #endif
48 return -errno;
49 }
50
dev_close(void)51 void dev_close(void)
52 {
53 close(erofs_devfd);
54 erofs_devname = NULL;
55 erofs_devfd = -1;
56 erofs_devsz = 0;
57 }
58
dev_open(const char * dev)59 int dev_open(const char *dev)
60 {
61 struct stat st;
62 int fd, ret;
63
64 fd = open(dev, O_RDWR | O_CREAT | O_BINARY, 0644);
65 if (fd < 0) {
66 erofs_err("failed to open(%s).", dev);
67 return -errno;
68 }
69
70 ret = fstat(fd, &st);
71 if (ret) {
72 erofs_err("failed to fstat(%s).", dev);
73 close(fd);
74 return -errno;
75 }
76
77 switch (st.st_mode & S_IFMT) {
78 case S_IFBLK:
79 ret = dev_get_blkdev_size(fd, &erofs_devsz);
80 if (ret) {
81 erofs_err("failed to get block device size(%s).", dev);
82 close(fd);
83 return ret;
84 }
85 erofs_devsz = round_down(erofs_devsz, EROFS_BLKSIZ);
86 break;
87 case S_IFREG:
88 ret = ftruncate(fd, 0);
89 if (ret) {
90 erofs_err("failed to ftruncate(%s).", dev);
91 close(fd);
92 return -errno;
93 }
94 /* INT64_MAX is the limit of kernel vfs */
95 erofs_devsz = INT64_MAX;
96 break;
97 default:
98 erofs_err("bad file type (%s, %o).", dev, st.st_mode);
99 close(fd);
100 return -EINVAL;
101 }
102
103 erofs_devname = dev;
104 erofs_devfd = fd;
105
106 erofs_info("successfully to open %s", dev);
107 return 0;
108 }
109
blob_closeall(void)110 void blob_closeall(void)
111 {
112 unsigned int i;
113
114 for (i = 0; i < erofs_nblobs; ++i)
115 close(erofs_blobfd[i]);
116 erofs_nblobs = 0;
117 }
118
blob_open_ro(const char * dev)119 int blob_open_ro(const char *dev)
120 {
121 int fd = open(dev, O_RDONLY | O_BINARY);
122
123 if (fd < 0) {
124 erofs_err("failed to open(%s).", dev);
125 return -errno;
126 }
127
128 erofs_blobfd[erofs_nblobs] = fd;
129 erofs_info("successfully to open blob%u %s", erofs_nblobs, dev);
130 ++erofs_nblobs;
131 return 0;
132 }
133
134 /* XXX: temporary soluation. Disk I/O implementation needs to be refactored. */
dev_open_ro(const char * dev)135 int dev_open_ro(const char *dev)
136 {
137 int fd = open(dev, O_RDONLY | O_BINARY);
138
139 if (fd < 0) {
140 erofs_err("failed to open(%s).", dev);
141 return -errno;
142 }
143
144 erofs_devfd = fd;
145 erofs_devname = dev;
146 erofs_devsz = INT64_MAX;
147 return 0;
148 }
149
dev_length(void)150 u64 dev_length(void)
151 {
152 return erofs_devsz;
153 }
154
dev_write(const void * buf,u64 offset,size_t len)155 int dev_write(const void *buf, u64 offset, size_t len)
156 {
157 int ret;
158
159 if (cfg.c_dry_run)
160 return 0;
161
162 if (!buf) {
163 erofs_err("buf is NULL");
164 return -EINVAL;
165 }
166
167 if (offset >= erofs_devsz || len > erofs_devsz ||
168 offset > erofs_devsz - len) {
169 erofs_err("Write posion[%" PRIu64 ", %zd] is too large beyond the end of device(%" PRIu64 ").",
170 offset, len, erofs_devsz);
171 return -EINVAL;
172 }
173
174 #ifdef HAVE_PWRITE64
175 ret = pwrite64(erofs_devfd, buf, len, (off64_t)offset);
176 #else
177 ret = pwrite(erofs_devfd, buf, len, (off_t)offset);
178 #endif
179 if (ret != (int)len) {
180 if (ret < 0) {
181 erofs_err("Failed to write data into device - %s:[%" PRIu64 ", %zd].",
182 erofs_devname, offset, len);
183 return -errno;
184 }
185
186 erofs_err("Writing data into device - %s:[%" PRIu64 ", %zd] - was truncated.",
187 erofs_devname, offset, len);
188 return -ERANGE;
189 }
190 return 0;
191 }
192
dev_fillzero(u64 offset,size_t len,bool padding)193 int dev_fillzero(u64 offset, size_t len, bool padding)
194 {
195 static const char zero[EROFS_BLKSIZ] = {0};
196 int ret;
197
198 if (cfg.c_dry_run)
199 return 0;
200
201 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
202 if (!padding && fallocate(erofs_devfd, FALLOC_FL_PUNCH_HOLE |
203 FALLOC_FL_KEEP_SIZE, offset, len) >= 0)
204 return 0;
205 #endif
206 while (len > EROFS_BLKSIZ) {
207 ret = dev_write(zero, offset, EROFS_BLKSIZ);
208 if (ret)
209 return ret;
210 len -= EROFS_BLKSIZ;
211 offset += EROFS_BLKSIZ;
212 }
213 return dev_write(zero, offset, len);
214 }
215
dev_fsync(void)216 int dev_fsync(void)
217 {
218 int ret;
219
220 ret = fsync(erofs_devfd);
221 if (ret) {
222 erofs_err("Could not fsync device!!!");
223 return -EIO;
224 }
225 return 0;
226 }
227
dev_resize(unsigned int blocks)228 int dev_resize(unsigned int blocks)
229 {
230 int ret;
231 struct stat st;
232 u64 length;
233
234 if (cfg.c_dry_run || erofs_devsz != INT64_MAX)
235 return 0;
236
237 ret = fstat(erofs_devfd, &st);
238 if (ret) {
239 erofs_err("failed to fstat.");
240 return -errno;
241 }
242
243 length = (u64)blocks * EROFS_BLKSIZ;
244 if (st.st_size == length)
245 return 0;
246 if (st.st_size > length)
247 return ftruncate(erofs_devfd, length);
248
249 length = length - st.st_size;
250 #if defined(HAVE_FALLOCATE)
251 if (fallocate(erofs_devfd, 0, st.st_size, length) >= 0)
252 return 0;
253 #endif
254 return dev_fillzero(st.st_size, length, true);
255 }
256
dev_read(int device_id,void * buf,u64 offset,size_t len)257 int dev_read(int device_id, void *buf, u64 offset, size_t len)
258 {
259 int ret, fd;
260
261 if (cfg.c_dry_run)
262 return 0;
263
264 if (!buf) {
265 erofs_err("buf is NULL");
266 return -EINVAL;
267 }
268
269 if (!device_id) {
270 fd = erofs_devfd;
271 } else {
272 if (device_id > erofs_nblobs) {
273 erofs_err("invalid device id %d", device_id);
274 return -ENODEV;
275 }
276 fd = erofs_blobfd[device_id - 1];
277 }
278
279 #ifdef HAVE_PREAD64
280 ret = pread64(fd, buf, len, (off64_t)offset);
281 #else
282 ret = pread(fd, buf, len, (off_t)offset);
283 #endif
284 if (ret != (int)len) {
285 erofs_err("Failed to read data from device - %s:[%" PRIu64 ", %zd].",
286 erofs_devname, offset, len);
287 return -errno;
288 }
289 return 0;
290 }
291
__erofs_copy_file_range(int fd_in,erofs_off_t * off_in,int fd_out,erofs_off_t * off_out,size_t length)292 static ssize_t __erofs_copy_file_range(int fd_in, erofs_off_t *off_in,
293 int fd_out, erofs_off_t *off_out,
294 size_t length)
295 {
296 size_t copied = 0;
297 char buf[8192];
298
299 /*
300 * Main copying loop. The buffer size is arbitrary and is a
301 * trade-off between stack size consumption, cache usage, and
302 * amortization of system call overhead.
303 */
304 while (length > 0) {
305 size_t to_read;
306 ssize_t read_count;
307 char *end, *p;
308
309 to_read = min_t(size_t, length, sizeof(buf));
310 #ifdef HAVE_PREAD64
311 read_count = pread64(fd_in, buf, to_read, *off_in);
312 #else
313 read_count = pread(fd_in, buf, to_read, *off_in);
314 #endif
315 if (read_count == 0)
316 /* End of file reached prematurely. */
317 return copied;
318 if (read_count < 0) {
319 /* Report the number of bytes copied so far. */
320 if (copied > 0)
321 return copied;
322 return -1;
323 }
324 *off_in += read_count;
325
326 /* Write the buffer part which was read to the destination. */
327 end = buf + read_count;
328 for (p = buf; p < end; ) {
329 ssize_t write_count;
330
331 #ifdef HAVE_PWRITE64
332 write_count = pwrite64(fd_out, p, end - p, *off_out);
333 #else
334 write_count = pwrite(fd_out, p, end - p, *off_out);
335 #endif
336 if (write_count < 0) {
337 /*
338 * Adjust the input read position to match what
339 * we have written, so that the caller can pick
340 * up after the error.
341 */
342 size_t written = p - buf;
343 /*
344 * NB: This needs to be signed so that we can
345 * form the negative value below.
346 */
347 ssize_t overread = read_count - written;
348
349 *off_in -= overread;
350 /* Report the number of bytes copied so far. */
351 if (copied + written > 0)
352 return copied + written;
353 return -1;
354 }
355 p += write_count;
356 *off_out += write_count;
357 } /* Write loop. */
358 copied += read_count;
359 length -= read_count;
360 }
361 return copied;
362 }
363
erofs_copy_file_range(int fd_in,erofs_off_t * off_in,int fd_out,erofs_off_t * off_out,size_t length)364 ssize_t erofs_copy_file_range(int fd_in, erofs_off_t *off_in,
365 int fd_out, erofs_off_t *off_out,
366 size_t length)
367 {
368 #ifdef HAVE_COPY_FILE_RANGE
369 off64_t off64_in = *off_in, off64_out = *off_out;
370 ssize_t ret;
371
372 ret = copy_file_range(fd_in, &off64_in, fd_out, &off64_out,
373 length, 0);
374 if (ret >= 0)
375 goto out;
376 if (errno != ENOSYS) {
377 ret = -errno;
378 out:
379 *off_in = off64_in;
380 *off_out = off64_out;
381 return ret;
382 }
383 #endif
384 return __erofs_copy_file_range(fd_in, off_in, fd_out, off_out, length);
385 }
386