• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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