1 /* 2 * 9p utilities 3 * 4 * Copyright IBM, Corp. 2017 5 * 6 * Authors: 7 * Greg Kurz <groug@kaod.org> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 */ 12 13 #ifndef QEMU_9P_UTIL_H 14 #define QEMU_9P_UTIL_H 15 16 #ifdef O_PATH 17 #define O_PATH_9P_UTIL O_PATH 18 #else 19 #define O_PATH_9P_UTIL 0 20 #endif 21 22 #ifdef CONFIG_DARWIN 23 #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0) 24 #define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW) 25 #define qemu_llistxattr(...) listxattr(__VA_ARGS__, XATTR_NOFOLLOW) 26 #define qemu_lremovexattr(...) removexattr(__VA_ARGS__, XATTR_NOFOLLOW) 27 static inline int qemu_lsetxattr(const char *path, const char *name, 28 const void *value, size_t size, int flags) { 29 return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW); 30 } 31 #else 32 #define qemu_fgetxattr fgetxattr 33 #define qemu_lgetxattr lgetxattr 34 #define qemu_llistxattr llistxattr 35 #define qemu_lremovexattr lremovexattr 36 #define qemu_lsetxattr lsetxattr 37 #endif 38 39 static inline void close_preserve_errno(int fd) 40 { 41 int serrno = errno; 42 close(fd); 43 errno = serrno; 44 } 45 46 static inline int openat_dir(int dirfd, const char *name) 47 { 48 return openat(dirfd, name, 49 O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL); 50 } 51 52 static inline int openat_file(int dirfd, const char *name, int flags, 53 mode_t mode) 54 { 55 int fd, serrno, ret; 56 57 #ifndef CONFIG_DARWIN 58 again: 59 #endif 60 fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK, 61 mode); 62 if (fd == -1) { 63 #ifndef CONFIG_DARWIN 64 if (errno == EPERM && (flags & O_NOATIME)) { 65 /* 66 * The client passed O_NOATIME but we lack permissions to honor it. 67 * Rather than failing the open, fall back without O_NOATIME. This 68 * doesn't break the semantics on the client side, as the Linux 69 * open(2) man page notes that O_NOATIME "may not be effective on 70 * all filesystems". In particular, NFS and other network 71 * filesystems ignore it entirely. 72 */ 73 flags &= ~O_NOATIME; 74 goto again; 75 } 76 #endif 77 return -1; 78 } 79 80 serrno = errno; 81 /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't 82 * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat() 83 * ignored it anyway. 84 */ 85 if (!(flags & O_PATH_9P_UTIL)) { 86 ret = fcntl(fd, F_SETFL, flags); 87 assert(!ret); 88 } 89 errno = serrno; 90 return fd; 91 } 92 93 ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name, 94 void *value, size_t size); 95 int fsetxattrat_nofollow(int dirfd, const char *path, const char *name, 96 void *value, size_t size, int flags); 97 ssize_t flistxattrat_nofollow(int dirfd, const char *filename, 98 char *list, size_t size); 99 ssize_t fremovexattrat_nofollow(int dirfd, const char *filename, 100 const char *name); 101 102 /* 103 * Darwin has d_seekoff, which appears to function similarly to d_off. 104 * However, it does not appear to be supported on all file systems, 105 * so ensure it is manually injected earlier and call here when 106 * needed. 107 */ 108 static inline off_t qemu_dirent_off(struct dirent *dent) 109 { 110 #ifdef CONFIG_DARWIN 111 return dent->d_seekoff; 112 #else 113 return dent->d_off; 114 #endif 115 } 116 117 /** 118 * qemu_dirent_dup() - Duplicate directory entry @dent. 119 * 120 * @dent: original directory entry to be duplicated 121 * Return: duplicated directory entry which should be freed with g_free() 122 * 123 * It is highly recommended to use this function instead of open coding 124 * duplication of dirent objects, because the actual struct dirent 125 * size may be bigger or shorter than sizeof(struct dirent) and correct 126 * handling is platform specific (see gitlab issue #841). 127 */ 128 static inline struct dirent *qemu_dirent_dup(struct dirent *dent) 129 { 130 size_t sz = 0; 131 #if defined _DIRENT_HAVE_D_RECLEN 132 /* Avoid use of strlen() if platform supports d_reclen. */ 133 sz = dent->d_reclen; 134 #endif 135 /* 136 * Test sz for zero even if d_reclen is available 137 * because some drivers may set d_reclen to zero. 138 */ 139 if (sz == 0) { 140 /* Fallback to the most portable way. */ 141 sz = offsetof(struct dirent, d_name) + 142 strlen(dent->d_name) + 1; 143 } 144 return g_memdup(dent, sz); 145 } 146 147 /* 148 * As long as mknodat is not available on macOS, this workaround 149 * using pthread_fchdir_np is needed. qemu_mknodat is defined in 150 * os-posix.c. pthread_fchdir_np is weakly linked here as a guard 151 * in case it disappears in future macOS versions, because it is 152 * is a private API. 153 */ 154 #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP 155 int pthread_fchdir_np(int fd) __attribute__((weak_import)); 156 #endif 157 int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev); 158 159 #endif 160