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