qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

9p-util.h (7139B)


      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 #if !defined(CONFIG_LINUX)
     23 
     24 /*
     25  * Generates a Linux device number (a.k.a. dev_t) for given device major
     26  * and minor numbers.
     27  *
     28  * To be more precise: it generates a device number in glibc's format
     29  * (MMMM_Mmmm_mmmM_MMmm, 64 bits) actually, which is compatible with
     30  * Linux's format (mmmM_MMmm, 32 bits), as described in <bits/sysmacros.h>.
     31  */
     32 static inline uint64_t makedev_dotl(uint32_t dev_major, uint32_t dev_minor)
     33 {
     34     uint64_t dev;
     35 
     36     // from glibc sysmacros.h:
     37     dev  = (((uint64_t) (dev_major & 0x00000fffu)) <<  8);
     38     dev |= (((uint64_t) (dev_major & 0xfffff000u)) << 32);
     39     dev |= (((uint64_t) (dev_minor & 0x000000ffu)) <<  0);
     40     dev |= (((uint64_t) (dev_minor & 0xffffff00u)) << 12);
     41     return dev;
     42 }
     43 
     44 #endif
     45 
     46 /*
     47  * Converts given device number from host's device number format to Linux
     48  * device number format. As both the size of type dev_t and encoding of
     49  * dev_t is system dependant, we have to convert them for Linux guests if
     50  * host is not running Linux.
     51  */
     52 static inline uint64_t host_dev_to_dotl_dev(dev_t dev)
     53 {
     54 #ifdef CONFIG_LINUX
     55     return dev;
     56 #else
     57     return makedev_dotl(major(dev), minor(dev));
     58 #endif
     59 }
     60 
     61 /* Translates errno from host -> Linux if needed */
     62 static inline int errno_to_dotl(int err) {
     63 #if defined(CONFIG_LINUX)
     64     /* nothing to translate (Linux -> Linux) */
     65 #elif defined(CONFIG_DARWIN)
     66     /*
     67      * translation mandatory for macOS hosts
     68      *
     69      * FIXME: Only most important errnos translated here yet, this should be
     70      * extended to as many errnos being translated as possible in future.
     71      */
     72     if (err == ENAMETOOLONG) {
     73         err = 36; /* ==ENAMETOOLONG on Linux */
     74     } else if (err == ENOTEMPTY) {
     75         err = 39; /* ==ENOTEMPTY on Linux */
     76     } else if (err == ELOOP) {
     77         err = 40; /* ==ELOOP on Linux */
     78     } else if (err == ENOATTR) {
     79         err = 61; /* ==ENODATA on Linux */
     80     } else if (err == ENOTSUP) {
     81         err = 95; /* ==EOPNOTSUPP on Linux */
     82     } else if (err == EOPNOTSUPP) {
     83         err = 95; /* ==EOPNOTSUPP on Linux */
     84     }
     85 #else
     86 #error Missing errno translation to Linux for this host system
     87 #endif
     88     return err;
     89 }
     90 
     91 #ifdef CONFIG_DARWIN
     92 #define qemu_fgetxattr(...) fgetxattr(__VA_ARGS__, 0, 0)
     93 #define qemu_lgetxattr(...) getxattr(__VA_ARGS__, 0, XATTR_NOFOLLOW)
     94 #define qemu_llistxattr(...) listxattr(__VA_ARGS__, XATTR_NOFOLLOW)
     95 #define qemu_lremovexattr(...) removexattr(__VA_ARGS__, XATTR_NOFOLLOW)
     96 static inline int qemu_lsetxattr(const char *path, const char *name,
     97                                  const void *value, size_t size, int flags) {
     98     return setxattr(path, name, value, size, 0, flags | XATTR_NOFOLLOW);
     99 }
    100 #else
    101 #define qemu_fgetxattr fgetxattr
    102 #define qemu_lgetxattr lgetxattr
    103 #define qemu_llistxattr llistxattr
    104 #define qemu_lremovexattr lremovexattr
    105 #define qemu_lsetxattr lsetxattr
    106 #endif
    107 
    108 static inline void close_preserve_errno(int fd)
    109 {
    110     int serrno = errno;
    111     close(fd);
    112     errno = serrno;
    113 }
    114 
    115 static inline int openat_dir(int dirfd, const char *name)
    116 {
    117     return openat(dirfd, name,
    118                   O_DIRECTORY | O_RDONLY | O_NOFOLLOW | O_PATH_9P_UTIL);
    119 }
    120 
    121 static inline int openat_file(int dirfd, const char *name, int flags,
    122                               mode_t mode)
    123 {
    124     int fd, serrno, ret;
    125 
    126 #ifndef CONFIG_DARWIN
    127 again:
    128 #endif
    129     fd = openat(dirfd, name, flags | O_NOFOLLOW | O_NOCTTY | O_NONBLOCK,
    130                 mode);
    131     if (fd == -1) {
    132 #ifndef CONFIG_DARWIN
    133         if (errno == EPERM && (flags & O_NOATIME)) {
    134             /*
    135              * The client passed O_NOATIME but we lack permissions to honor it.
    136              * Rather than failing the open, fall back without O_NOATIME. This
    137              * doesn't break the semantics on the client side, as the Linux
    138              * open(2) man page notes that O_NOATIME "may not be effective on
    139              * all filesystems". In particular, NFS and other network
    140              * filesystems ignore it entirely.
    141              */
    142             flags &= ~O_NOATIME;
    143             goto again;
    144         }
    145 #endif
    146         return -1;
    147     }
    148 
    149     serrno = errno;
    150     /* O_NONBLOCK was only needed to open the file. Let's drop it. We don't
    151      * do that with O_PATH since fcntl(F_SETFL) isn't supported, and openat()
    152      * ignored it anyway.
    153      */
    154     if (!(flags & O_PATH_9P_UTIL)) {
    155         ret = fcntl(fd, F_SETFL, flags);
    156         assert(!ret);
    157     }
    158     errno = serrno;
    159     return fd;
    160 }
    161 
    162 ssize_t fgetxattrat_nofollow(int dirfd, const char *path, const char *name,
    163                              void *value, size_t size);
    164 int fsetxattrat_nofollow(int dirfd, const char *path, const char *name,
    165                          void *value, size_t size, int flags);
    166 ssize_t flistxattrat_nofollow(int dirfd, const char *filename,
    167                               char *list, size_t size);
    168 ssize_t fremovexattrat_nofollow(int dirfd, const char *filename,
    169                                 const char *name);
    170 
    171 /*
    172  * Darwin has d_seekoff, which appears to function similarly to d_off.
    173  * However, it does not appear to be supported on all file systems,
    174  * so ensure it is manually injected earlier and call here when
    175  * needed.
    176  */
    177 static inline off_t qemu_dirent_off(struct dirent *dent)
    178 {
    179 #ifdef CONFIG_DARWIN
    180     return dent->d_seekoff;
    181 #else
    182     return dent->d_off;
    183 #endif
    184 }
    185 
    186 /**
    187  * qemu_dirent_dup() - Duplicate directory entry @dent.
    188  *
    189  * @dent: original directory entry to be duplicated
    190  * Return: duplicated directory entry which should be freed with g_free()
    191  *
    192  * It is highly recommended to use this function instead of open coding
    193  * duplication of dirent objects, because the actual struct dirent
    194  * size may be bigger or shorter than sizeof(struct dirent) and correct
    195  * handling is platform specific (see gitlab issue #841).
    196  */
    197 static inline struct dirent *qemu_dirent_dup(struct dirent *dent)
    198 {
    199     size_t sz = 0;
    200 #if defined _DIRENT_HAVE_D_RECLEN
    201     /* Avoid use of strlen() if platform supports d_reclen. */
    202     sz = dent->d_reclen;
    203 #endif
    204     /*
    205      * Test sz for zero even if d_reclen is available
    206      * because some drivers may set d_reclen to zero.
    207      */
    208     if (sz == 0) {
    209         /* Fallback to the most portable way. */
    210         sz = offsetof(struct dirent, d_name) +
    211                       strlen(dent->d_name) + 1;
    212     }
    213     return g_memdup(dent, sz);
    214 }
    215 
    216 /*
    217  * As long as mknodat is not available on macOS, this workaround
    218  * using pthread_fchdir_np is needed. qemu_mknodat is defined in
    219  * os-posix.c. pthread_fchdir_np is weakly linked here as a guard
    220  * in case it disappears in future macOS versions, because it is
    221  * is a private API.
    222  */
    223 #if defined CONFIG_DARWIN && defined CONFIG_PTHREAD_FCHDIR_NP
    224 int pthread_fchdir_np(int fd) __attribute__((weak_import));
    225 #endif
    226 int qemu_mknodat(int dirfd, const char *filename, mode_t mode, dev_t dev);
    227 
    228 #endif