1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * erofs-utils/lib/io.c
4  *
5  * Copyright (C) 2018 HUAWEI, Inc.
6  *             http://www.huawei.com/
7  * Created by Li Guifu <bluce.liguifu@huawei.com>
8  */
9 #ifndef _LARGEFILE64_SOURCE
10 #define _LARGEFILE64_SOURCE
11 #endif
12 #ifndef _GNU_SOURCE
13 #define _GNU_SOURCE
14 #endif
15 #include <sys/stat.h>
16 #include <sys/ioctl.h>
17 #include "erofs/io.h"
18 #ifdef HAVE_LINUX_FS_H
19 #include <linux/fs.h>
20 #endif
21 #ifdef HAVE_LINUX_FALLOC_H
22 #include <linux/falloc.h>
23 #endif
24 
25 #define pr_fmt(fmt) "EROFS IO: " FUNC_LINE_FMT fmt "\n"
26 #include "erofs/print.h"
27 
28 static const char *erofs_devname;
29 static int erofs_devfd = -1;
30 static u64 erofs_devsz;
31 
dev_get_blkdev_size(int fd,u64 * bytes)32 int dev_get_blkdev_size(int fd, u64 *bytes)
33 {
34 	errno = ENOTSUP;
35 #ifdef BLKGETSIZE64
36 	if (ioctl(fd, BLKGETSIZE64, bytes) >= 0)
37 		return 0;
38 #endif
39 
40 #ifdef BLKGETSIZE
41 	{
42 		unsigned long size;
43 		if (ioctl(fd, BLKGETSIZE, &size) >= 0) {
44 			*bytes = ((u64)size << 9);
45 			return 0;
46 		}
47 	}
48 #endif
49 	return -errno;
50 }
51 
dev_close(void)52 void dev_close(void)
53 {
54 	close(erofs_devfd);
55 	erofs_devname = NULL;
56 	erofs_devfd   = -1;
57 	erofs_devsz   = 0;
58 }
59 
dev_open(const char * dev)60 int dev_open(const char *dev)
61 {
62 	struct stat st;
63 	int fd, ret;
64 
65 	fd = open(dev, O_RDWR | O_CREAT | O_BINARY, 0644);
66 	if (fd < 0) {
67 		erofs_err("failed to open(%s).", dev);
68 		return -errno;
69 	}
70 
71 	ret = fstat(fd, &st);
72 	if (ret) {
73 		erofs_err("failed to fstat(%s).", dev);
74 		close(fd);
75 		return -errno;
76 	}
77 
78 	switch (st.st_mode & S_IFMT) {
79 	case S_IFBLK:
80 		ret = dev_get_blkdev_size(fd, &erofs_devsz);
81 		if (ret) {
82 			erofs_err("failed to get block device size(%s).", dev);
83 			close(fd);
84 			return ret;
85 		}
86 		erofs_devsz = round_down(erofs_devsz, EROFS_BLKSIZ);
87 		break;
88 	case S_IFREG:
89 		ret = ftruncate(fd, 0);
90 		if (ret) {
91 			erofs_err("failed to ftruncate(%s).", dev);
92 			close(fd);
93 			return -errno;
94 		}
95 		/* INT64_MAX is the limit of kernel vfs */
96 		erofs_devsz = INT64_MAX;
97 		break;
98 	default:
99 		erofs_err("bad file type (%s, %o).", dev, st.st_mode);
100 		close(fd);
101 		return -EINVAL;
102 	}
103 
104 	erofs_devname = dev;
105 	erofs_devfd = fd;
106 
107 	erofs_info("successfully to open %s", dev);
108 	return 0;
109 }
110 
111 /* XXX: temporary soluation. Disk I/O implementation needs to be refactored. */
dev_open_ro(const char * dev)112 int dev_open_ro(const char *dev)
113 {
114 	int fd = open(dev, O_RDONLY | O_BINARY);
115 
116 	if (fd < 0) {
117 		erofs_err("failed to open(%s).", dev);
118 		return -errno;
119 	}
120 
121 	erofs_devfd = fd;
122 	erofs_devname = dev;
123 	erofs_devsz = INT64_MAX;
124 	return 0;
125 }
126 
dev_length(void)127 u64 dev_length(void)
128 {
129 	return erofs_devsz;
130 }
131 
dev_write(const void * buf,u64 offset,size_t len)132 int dev_write(const void *buf, u64 offset, size_t len)
133 {
134 	int ret;
135 
136 	if (cfg.c_dry_run)
137 		return 0;
138 
139 	if (!buf) {
140 		erofs_err("buf is NULL");
141 		return -EINVAL;
142 	}
143 
144 	if (offset >= erofs_devsz || len > erofs_devsz ||
145 	    offset > erofs_devsz - len) {
146 		erofs_err("Write posion[%" PRIu64 ", %zd] is too large beyond the end of device(%" PRIu64 ").",
147 			  offset, len, erofs_devsz);
148 		return -EINVAL;
149 	}
150 
151 	ret = pwrite64(erofs_devfd, buf, len, (off64_t)offset);
152 	if (ret != (int)len) {
153 		if (ret < 0) {
154 			erofs_err("Failed to write data into device - %s:[%" PRIu64 ", %zd].",
155 				  erofs_devname, offset, len);
156 			return -errno;
157 		}
158 
159 		erofs_err("Writing data into device - %s:[%" PRIu64 ", %zd] - was truncated.",
160 			  erofs_devname, offset, len);
161 		return -ERANGE;
162 	}
163 	return 0;
164 }
165 
dev_fillzero(u64 offset,size_t len,bool padding)166 int dev_fillzero(u64 offset, size_t len, bool padding)
167 {
168 	static const char zero[EROFS_BLKSIZ] = {0};
169 	int ret;
170 
171 	if (cfg.c_dry_run)
172 		return 0;
173 
174 #if defined(HAVE_FALLOCATE) && defined(FALLOC_FL_PUNCH_HOLE)
175 	if (!padding && fallocate(erofs_devfd, FALLOC_FL_PUNCH_HOLE |
176 				  FALLOC_FL_KEEP_SIZE, offset, len) >= 0)
177 		return 0;
178 #endif
179 	while (len > EROFS_BLKSIZ) {
180 		ret = dev_write(zero, offset, EROFS_BLKSIZ);
181 		if (ret)
182 			return ret;
183 		len -= EROFS_BLKSIZ;
184 		offset += EROFS_BLKSIZ;
185 	}
186 	return dev_write(zero, offset, len);
187 }
188 
dev_fsync(void)189 int dev_fsync(void)
190 {
191 	int ret;
192 
193 	ret = fsync(erofs_devfd);
194 	if (ret) {
195 		erofs_err("Could not fsync device!!!");
196 		return -EIO;
197 	}
198 	return 0;
199 }
200 
dev_resize(unsigned int blocks)201 int dev_resize(unsigned int blocks)
202 {
203 	int ret;
204 	struct stat st;
205 	u64 length;
206 
207 	if (cfg.c_dry_run || erofs_devsz != INT64_MAX)
208 		return 0;
209 
210 	ret = fstat(erofs_devfd, &st);
211 	if (ret) {
212 		erofs_err("failed to fstat.");
213 		return -errno;
214 	}
215 
216 	length = (u64)blocks * EROFS_BLKSIZ;
217 	if (st.st_size == length)
218 		return 0;
219 	if (st.st_size > length)
220 		return ftruncate(erofs_devfd, length);
221 
222 	length = length - st.st_size;
223 #if defined(HAVE_FALLOCATE)
224 	if (fallocate(erofs_devfd, 0, st.st_size, length) >= 0)
225 		return 0;
226 #endif
227 	return dev_fillzero(st.st_size, length, true);
228 }
229 
dev_read(void * buf,u64 offset,size_t len)230 int dev_read(void *buf, u64 offset, size_t len)
231 {
232 	int ret;
233 
234 	if (cfg.c_dry_run)
235 		return 0;
236 
237 	if (!buf) {
238 		erofs_err("buf is NULL");
239 		return -EINVAL;
240 	}
241 	if (offset >= erofs_devsz || len > erofs_devsz ||
242 	    offset > erofs_devsz - len) {
243 		erofs_err("read posion[%" PRIu64 ", %zd] is too large beyond"
244 			  "the end of device(%" PRIu64 ").",
245 			  offset, len, erofs_devsz);
246 		return -EINVAL;
247 	}
248 
249 	ret = pread64(erofs_devfd, buf, len, (off64_t)offset);
250 	if (ret != (int)len) {
251 		erofs_err("Failed to read data from device - %s:[%" PRIu64 ", %zd].",
252 			  erofs_devname, offset, len);
253 		return -errno;
254 	}
255 	return 0;
256 }
257