/*1* Copyright (c) 2014 Landon Fuller <[email protected]>2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer9* in this position and unchanged.10* 2. Redistributions in binary form must reproduce the above copyright11* notice, this list of conditions and the following disclaimer in the12* documentation and/or other materials provided with the distribution.13*14* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR15* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES16* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.17* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,18* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT19* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,20* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY21* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT22* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF23* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.24*/2526#include <sys/param.h>2728#include <bsd_compat.h>29#include <assert.h>30#include <fcntl.h>31#include <pthread.h>32#include <stdarg.h>33#include <unistd.h>3435#if !HAVE_UNLINKAT || !HAVE_FSTATAT3637static pthread_mutex_t file_at_lock = PTHREAD_MUTEX_INITIALIZER;38static int file_at_dfd = -1;39static char saved_cwd[MAXPATHLEN];4041/**42* Acquire the cwd mutex and perform fchdir(dfd).43*44* On error, the mutex will be released automatically45* and a non-zero value will be returned.46*47* @param dfd The directory file descriptor to be passed to fchdir(), or48* AT_FDCWD to use the current working directory.49* @return The fchdir() result.50*/51static int52file_chdir_lock(int dfd)53{54int ret;5556pthread_mutex_lock(&file_at_lock);5758if (getcwd(saved_cwd, sizeof(saved_cwd)) == NULL)59saved_cwd[0] = '\0';6061assert(file_at_dfd == -1);62file_at_dfd = dfd;6364if (dfd == AT_FDCWD)65return 0;6667ret = fchdir(dfd);68if (ret != 0) {69pthread_mutex_unlock(&file_at_lock);70return ret;71} else {72return ret;73}74}7576/**77* Release the cwd mutex.78*/79static void80file_chdir_unlock(int dfd)81{82assert(file_at_dfd == dfd);83file_at_dfd = -1;8485if (dfd == AT_FDCWD)86return;8788if (saved_cwd[0] != '\0')89chdir(saved_cwd);90pthread_mutex_unlock(&file_at_lock);91}92#endif9394#if !HAVE_FACCESSAT95int96faccessat(int fd, const char *path, int mode, int flag)97{98int ret;99char saved_cwd[MAXPATHLEN];100const char *cwd;101102if ((cwd = getcwd(saved_cwd, sizeof(saved_cwd))) == NULL)103return (-1);104105if ((ret = file_chdir_lock(fd) != 0))106return ret;107108if (flag & AT_EACCESS) {109ret = eaccess(path, mode);110} else {111ret = access(path, mode);112}113114file_chdir_unlock(fd);115return ret;116}117#endif118119#if !HAVE_READLINKAT120ssize_t121readlinkat(int fd, const char *restrict path, char *restrict buf,122size_t bufsize)123{124int ret;125126if ((ret = file_chdir_lock(fd) != 0))127return ret;128129ret = readlink(path, buf, bufsize);130131file_chdir_unlock(fd);132return ret;133}134#endif135136#if !HAVE_FSTATAT137int138fstatat(int fd, const char *path, struct stat *buf, int flag)139{140int ret;141142if ((ret = file_chdir_lock(fd) != 0))143return ret;144145if (flag & AT_SYMLINK_NOFOLLOW) {146ret = lstat(path, buf);147} else {148ret = stat(path, buf);149}150151file_chdir_unlock(fd);152return ret;153}154#endif155156#if !HAVE_OPENAT157int158openat(int fd, const char *path, int flags, ...)159{160int ret;161va_list ap;162163if ((ret = file_chdir_lock(fd) != 0))164return ret;165166if (flags & O_CREAT) {167va_start(ap, flags);168ret = open(path, flags, va_arg(ap, int));169va_end(ap);170} else {171ret = open(path, flags);172}173174file_chdir_unlock(fd);175return ret;176}177#endif178179#if !HAVE_UNLINKAT180int181unlinkat(int fd, const char *path, int flag)182{183int ret;184185if ((ret = file_chdir_lock(fd) != 0))186return ret;187188if (flag & AT_REMOVEDIR) {189ret = rmdir(path);190} else {191ret = unlink(path);192}193194file_chdir_unlock(fd);195return ret;196}197#endif198199200