1 | /* |
2 | * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) |
3 | * Licensed under the GPL |
4 | */ |
5 | |
6 | #include <stdio.h> |
7 | #include <stddef.h> |
8 | #include <unistd.h> |
9 | #include <dirent.h> |
10 | #include <errno.h> |
11 | #include <fcntl.h> |
12 | #include <string.h> |
13 | #include <sys/stat.h> |
14 | #include <sys/time.h> |
15 | #include <sys/types.h> |
16 | #include <sys/vfs.h> |
17 | #include <sys/syscall.h> |
18 | #include "hostfs.h" |
19 | #include <utime.h> |
20 | |
21 | static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p) |
22 | { |
23 | p->ino = buf->st_ino; |
24 | p->mode = buf->st_mode; |
25 | p->nlink = buf->st_nlink; |
26 | p->uid = buf->st_uid; |
27 | p->gid = buf->st_gid; |
28 | p->size = buf->st_size; |
29 | p->atime.tv_sec = buf->st_atime; |
30 | p->atime.tv_nsec = 0; |
31 | p->ctime.tv_sec = buf->st_ctime; |
32 | p->ctime.tv_nsec = 0; |
33 | p->mtime.tv_sec = buf->st_mtime; |
34 | p->mtime.tv_nsec = 0; |
35 | p->blksize = buf->st_blksize; |
36 | p->blocks = buf->st_blocks; |
37 | p->maj = os_major(buf->st_rdev); |
38 | p->min = os_minor(buf->st_rdev); |
39 | p->dev = buf->st_dev; |
40 | } |
41 | |
42 | int stat_file(const char *path, struct hostfs_stat *p, int fd) |
43 | { |
44 | struct stat64 buf; |
45 | |
46 | if (fd >= 0) { |
47 | if (fstat64(fd, &buf) < 0) |
48 | return -errno; |
49 | } else if (lstat64(path, &buf) < 0) { |
50 | return -errno; |
51 | } |
52 | stat64_to_hostfs(buf: &buf, p); |
53 | return 0; |
54 | } |
55 | |
56 | int access_file(char *path, int r, int w, int x) |
57 | { |
58 | int mode = 0; |
59 | |
60 | if (r) |
61 | mode = R_OK; |
62 | if (w) |
63 | mode |= W_OK; |
64 | if (x) |
65 | mode |= X_OK; |
66 | if (access(path, mode) != 0) |
67 | return -errno; |
68 | else return 0; |
69 | } |
70 | |
71 | int open_file(char *path, int r, int w, int append) |
72 | { |
73 | int mode = 0, fd; |
74 | |
75 | if (r && !w) |
76 | mode = O_RDONLY; |
77 | else if (!r && w) |
78 | mode = O_WRONLY; |
79 | else if (r && w) |
80 | mode = O_RDWR; |
81 | else panic("Impossible mode in open_file" ); |
82 | |
83 | if (append) |
84 | mode |= O_APPEND; |
85 | fd = open64(path, mode); |
86 | if (fd < 0) |
87 | return -errno; |
88 | else return fd; |
89 | } |
90 | |
91 | void *open_dir(char *path, int *err_out) |
92 | { |
93 | DIR *dir; |
94 | |
95 | dir = opendir(path); |
96 | *err_out = errno; |
97 | |
98 | return dir; |
99 | } |
100 | |
101 | void seek_dir(void *stream, unsigned long long pos) |
102 | { |
103 | DIR *dir = stream; |
104 | |
105 | seekdir(dir, pos); |
106 | } |
107 | |
108 | char *read_dir(void *stream, unsigned long long *pos_out, |
109 | unsigned long long *ino_out, int *len_out, |
110 | unsigned int *type_out) |
111 | { |
112 | DIR *dir = stream; |
113 | struct dirent *ent; |
114 | |
115 | ent = readdir(dir); |
116 | if (ent == NULL) |
117 | return NULL; |
118 | *len_out = strlen(ent->d_name); |
119 | *ino_out = ent->d_ino; |
120 | *type_out = ent->d_type; |
121 | *pos_out = ent->d_off; |
122 | return ent->d_name; |
123 | } |
124 | |
125 | int read_file(int fd, unsigned long long *offset, char *buf, int len) |
126 | { |
127 | int n; |
128 | |
129 | n = pread64(fd, buf, len, *offset); |
130 | if (n < 0) |
131 | return -errno; |
132 | *offset += n; |
133 | return n; |
134 | } |
135 | |
136 | int write_file(int fd, unsigned long long *offset, const char *buf, int len) |
137 | { |
138 | int n; |
139 | |
140 | n = pwrite64(fd, buf, len, *offset); |
141 | if (n < 0) |
142 | return -errno; |
143 | *offset += n; |
144 | return n; |
145 | } |
146 | |
147 | int lseek_file(int fd, long long offset, int whence) |
148 | { |
149 | int ret; |
150 | |
151 | ret = lseek64(fd, offset, whence); |
152 | if (ret < 0) |
153 | return -errno; |
154 | return 0; |
155 | } |
156 | |
157 | int fsync_file(int fd, int datasync) |
158 | { |
159 | int ret; |
160 | if (datasync) |
161 | ret = fdatasync(fd); |
162 | else |
163 | ret = fsync(fd); |
164 | |
165 | if (ret < 0) |
166 | return -errno; |
167 | return 0; |
168 | } |
169 | |
170 | int replace_file(int oldfd, int fd) |
171 | { |
172 | return dup2(oldfd, fd); |
173 | } |
174 | |
175 | void close_file(void *stream) |
176 | { |
177 | close(*((int *) stream)); |
178 | } |
179 | |
180 | void close_dir(void *stream) |
181 | { |
182 | closedir(stream); |
183 | } |
184 | |
185 | int file_create(char *name, int mode) |
186 | { |
187 | int fd; |
188 | |
189 | fd = open64(name, O_CREAT | O_RDWR, mode); |
190 | if (fd < 0) |
191 | return -errno; |
192 | return fd; |
193 | } |
194 | |
195 | int set_attr(const char *file, struct hostfs_iattr *attrs, int fd) |
196 | { |
197 | struct hostfs_stat st; |
198 | struct timeval times[2]; |
199 | int err, ma; |
200 | |
201 | if (attrs->ia_valid & HOSTFS_ATTR_MODE) { |
202 | if (fd >= 0) { |
203 | if (fchmod(fd, attrs->ia_mode) != 0) |
204 | return -errno; |
205 | } else if (chmod(file, attrs->ia_mode) != 0) { |
206 | return -errno; |
207 | } |
208 | } |
209 | if (attrs->ia_valid & HOSTFS_ATTR_UID) { |
210 | if (fd >= 0) { |
211 | if (fchown(fd, attrs->ia_uid, -1)) |
212 | return -errno; |
213 | } else if (chown(file, attrs->ia_uid, -1)) { |
214 | return -errno; |
215 | } |
216 | } |
217 | if (attrs->ia_valid & HOSTFS_ATTR_GID) { |
218 | if (fd >= 0) { |
219 | if (fchown(fd, -1, attrs->ia_gid)) |
220 | return -errno; |
221 | } else if (chown(file, -1, attrs->ia_gid)) { |
222 | return -errno; |
223 | } |
224 | } |
225 | if (attrs->ia_valid & HOSTFS_ATTR_SIZE) { |
226 | if (fd >= 0) { |
227 | if (ftruncate(fd, attrs->ia_size)) |
228 | return -errno; |
229 | } else if (truncate(file, attrs->ia_size)) { |
230 | return -errno; |
231 | } |
232 | } |
233 | |
234 | /* |
235 | * Update accessed and/or modified time, in two parts: first set |
236 | * times according to the changes to perform, and then call futimes() |
237 | * or utimes() to apply them. |
238 | */ |
239 | ma = (HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET); |
240 | if (attrs->ia_valid & ma) { |
241 | err = stat_file(path: file, p: &st, fd); |
242 | if (err != 0) |
243 | return err; |
244 | |
245 | times[0].tv_sec = st.atime.tv_sec; |
246 | times[0].tv_usec = st.atime.tv_nsec / 1000; |
247 | times[1].tv_sec = st.mtime.tv_sec; |
248 | times[1].tv_usec = st.mtime.tv_nsec / 1000; |
249 | |
250 | if (attrs->ia_valid & HOSTFS_ATTR_ATIME_SET) { |
251 | times[0].tv_sec = attrs->ia_atime.tv_sec; |
252 | times[0].tv_usec = attrs->ia_atime.tv_nsec / 1000; |
253 | } |
254 | if (attrs->ia_valid & HOSTFS_ATTR_MTIME_SET) { |
255 | times[1].tv_sec = attrs->ia_mtime.tv_sec; |
256 | times[1].tv_usec = attrs->ia_mtime.tv_nsec / 1000; |
257 | } |
258 | |
259 | if (fd >= 0) { |
260 | if (futimes(fd, times) != 0) |
261 | return -errno; |
262 | } else if (utimes(file, times) != 0) { |
263 | return -errno; |
264 | } |
265 | } |
266 | |
267 | /* Note: ctime is not handled */ |
268 | if (attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)) { |
269 | err = stat_file(path: file, p: &st, fd); |
270 | attrs->ia_atime = st.atime; |
271 | attrs->ia_mtime = st.mtime; |
272 | if (err != 0) |
273 | return err; |
274 | } |
275 | return 0; |
276 | } |
277 | |
278 | int make_symlink(const char *from, const char *to) |
279 | { |
280 | int err; |
281 | |
282 | err = symlink(to, from); |
283 | if (err) |
284 | return -errno; |
285 | return 0; |
286 | } |
287 | |
288 | int unlink_file(const char *file) |
289 | { |
290 | int err; |
291 | |
292 | err = unlink(file); |
293 | if (err) |
294 | return -errno; |
295 | return 0; |
296 | } |
297 | |
298 | int do_mkdir(const char *file, int mode) |
299 | { |
300 | int err; |
301 | |
302 | err = mkdir(file, mode); |
303 | if (err) |
304 | return -errno; |
305 | return 0; |
306 | } |
307 | |
308 | int hostfs_do_rmdir(const char *file) |
309 | { |
310 | int err; |
311 | |
312 | err = rmdir(file); |
313 | if (err) |
314 | return -errno; |
315 | return 0; |
316 | } |
317 | |
318 | int do_mknod(const char *file, int mode, unsigned int major, unsigned int minor) |
319 | { |
320 | int err; |
321 | |
322 | err = mknod(file, mode, os_makedev(major, minor)); |
323 | if (err) |
324 | return -errno; |
325 | return 0; |
326 | } |
327 | |
328 | int link_file(const char *to, const char *from) |
329 | { |
330 | int err; |
331 | |
332 | err = link(to, from); |
333 | if (err) |
334 | return -errno; |
335 | return 0; |
336 | } |
337 | |
338 | int hostfs_do_readlink(char *file, char *buf, int size) |
339 | { |
340 | int n; |
341 | |
342 | n = readlink(file, buf, size); |
343 | if (n < 0) |
344 | return -errno; |
345 | if (n < size) |
346 | buf[n] = '\0'; |
347 | return n; |
348 | } |
349 | |
350 | int rename_file(char *from, char *to) |
351 | { |
352 | int err; |
353 | |
354 | err = rename(from, to); |
355 | if (err < 0) |
356 | return -errno; |
357 | return 0; |
358 | } |
359 | |
360 | int rename2_file(char *from, char *to, unsigned int flags) |
361 | { |
362 | int err; |
363 | |
364 | #ifndef SYS_renameat2 |
365 | # ifdef __x86_64__ |
366 | # define SYS_renameat2 316 |
367 | # endif |
368 | # ifdef __i386__ |
369 | # define SYS_renameat2 353 |
370 | # endif |
371 | #endif |
372 | |
373 | #ifdef SYS_renameat2 |
374 | err = syscall(SYS_renameat2, AT_FDCWD, from, AT_FDCWD, to, flags); |
375 | if (err < 0) { |
376 | if (errno != ENOSYS) |
377 | return -errno; |
378 | else |
379 | return -EINVAL; |
380 | } |
381 | return 0; |
382 | #else |
383 | return -EINVAL; |
384 | #endif |
385 | } |
386 | |
387 | int do_statfs(char *root, long *bsize_out, long long *blocks_out, |
388 | long long *bfree_out, long long *bavail_out, |
389 | long long *files_out, long long *ffree_out, |
390 | void *fsid_out, int fsid_size, long *namelen_out) |
391 | { |
392 | struct statfs64 buf; |
393 | int err; |
394 | |
395 | err = statfs64(root, &buf); |
396 | if (err < 0) |
397 | return -errno; |
398 | |
399 | *bsize_out = buf.f_bsize; |
400 | *blocks_out = buf.f_blocks; |
401 | *bfree_out = buf.f_bfree; |
402 | *bavail_out = buf.f_bavail; |
403 | *files_out = buf.f_files; |
404 | *ffree_out = buf.f_ffree; |
405 | memcpy(fsid_out, &buf.f_fsid, |
406 | sizeof(buf.f_fsid) > fsid_size ? fsid_size : |
407 | sizeof(buf.f_fsid)); |
408 | *namelen_out = buf.f_namelen; |
409 | |
410 | return 0; |
411 | } |
412 | |