diff options
-rw-r--r-- | mysys/my_delete.c | 11 | ||||
-rw-r--r-- | mysys/my_open.c | 88 | ||||
-rw-r--r-- | mysys/my_symlink.c | 75 | ||||
-rw-r--r-- | mysys/mysys_priv.h | 28 |
4 files changed, 118 insertions, 84 deletions
diff --git a/mysys/my_delete.c b/mysys/my_delete.c index 155e925e9ba..8487f1d2e5a 100644 --- a/mysys/my_delete.c +++ b/mysys/my_delete.c @@ -21,6 +21,12 @@ static int my_win_unlink(const char *name); #endif +CREATE_NOSYMLINK_FUNCTION( + unlink_nosymlinks(const char *pathname), + unlinkat(dfd, filename, 0), + unlink(pathname) +); + int my_delete(const char *name, myf MyFlags) { int err; @@ -30,7 +36,10 @@ int my_delete(const char *name, myf MyFlags) #ifdef _WIN32 err = my_win_unlink(name); #else - err = unlink(name); + if (MyFlags & MY_NOSYMLINKS) + err= unlink_nosymlinks(name); + else + err= unlink(name); #endif if(err) diff --git a/mysys/my_open.c b/mysys/my_open.c index 0effc4bedda..cafb94ef558 100644 --- a/mysys/my_open.c +++ b/mysys/my_open.c @@ -18,11 +18,11 @@ #include <m_string.h> #include <errno.h> -#if !defined(O_PATH) && defined(O_EXEC) /* FreeBSD */ -#define O_PATH O_EXEC -#endif - -static int open_nosymlinks(const char *pathname, int flags, int mode); +CREATE_NOSYMLINK_FUNCTION( + open_nosymlinks(const char *pathname, int flags, int mode), + openat(dfd, filename, O_NOFOLLOW | flags, mode), + open(pathname, O_NOFOLLOW | flags, mode) +); /* Open a file @@ -182,81 +182,3 @@ void my_print_open_files(void) } #endif - -/** - like open(), but with symlinks are not accepted anywhere in the path - - This is used for opening symlinked tables for DATA/INDEX DIRECTORY. - The paths there have been realpath()-ed. So, we can assume here that - - * `pathname` is an absolute path - * no '.', '..', and '//' in the path - * file exists -*/ -static int open_nosymlinks(const char *pathname, int flags, int mode) -{ -#ifndef O_PATH -#ifdef HAVE_REALPATH - char buf[PATH_MAX+1]; - if (realpath(pathname, buf) == NULL) - return -1; - if (strcmp(pathname, buf)) - { - errno= ENOTDIR; - return -1; - } -#endif - return open(pathname, flags, mode | O_NOFOLLOW); -#else - - char buf[PATH_MAX+1]; - char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); - int fd, dfd= -1; - - if (*end) - { - errno= ENAMETOOLONG; - return -1; - } - - if (*s != '/') /* not an absolute path */ - { - errno= ENOENT; - return -1; - } - - for (;;) - { - if (*e == '/') /* '//' in the path */ - { - errno= ENOENT; - goto err; - } - while (*e && *e != '/') - e++; - *e= 0; - if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3)) - { - errno= ENOENT; - goto err; - } - - fd = openat(dfd, s, O_NOFOLLOW | (e < end ? O_PATH : flags), mode); - if (fd < 0) - goto err; - - if (dfd >= 0) - close(dfd); - - dfd= fd; - s= ++e; - - if (e >= end) - return fd; - } -err: - if (dfd >= 0) - close(dfd); - return -1; -#endif -} diff --git a/mysys/my_symlink.c b/mysys/my_symlink.c index bc01a68263d..ed35fff41e9 100644 --- a/mysys/my_symlink.c +++ b/mysys/my_symlink.c @@ -177,3 +177,78 @@ int my_realpath(char *to, const char *filename, myf MyFlags) #endif return 0; } + +#ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS +/** opens the parent dir. walks the path, and does not resolve symlinks + + returns the pointer to the file name (basename) within the pathname + or NULL in case of an error + + stores the parent dir (dirname) file descriptor in pdfd. + It can be -1 even if there was no error! + + This is used for symlinked tables for DATA/INDEX DIRECTORY. + The paths there have been realpath()-ed. So, we can assume here that + + * `pathname` is an absolute path + * no '.', '..', and '//' in the path + * file exists +*/ + +const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd) +{ + char buf[PATH_MAX+1]; + char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); + int fd, dfd= -1; + + if (*end) + { + errno= ENAMETOOLONG; + return NULL; + } + + if (*s != '/') /* not an absolute path */ + { + errno= ENOENT; + return NULL; + } + + for (;;) + { + if (*e == '/') /* '//' in the path */ + { + errno= ENOENT; + goto err; + } + while (*e && *e != '/') + e++; + *e= 0; + + if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3)) + { + errno= ENOENT; + goto err; + } + + if (++e >= end) + { + *pdfd= dfd; + return pathname + (s - buf); + } + + fd = openat(dfd, s, O_NOFOLLOW | O_PATH); + if (fd < 0) + goto err; + + if (dfd >= 0) + close(dfd); + + dfd= fd; + s= e; + } +err: + if (dfd >= 0) + close(dfd); + return NULL; +} +#endif diff --git a/mysys/mysys_priv.h b/mysys/mysys_priv.h index f5d2f301837..4b489504c26 100644 --- a/mysys/mysys_priv.h +++ b/mysys/mysys_priv.h @@ -89,6 +89,34 @@ void sf_free(void *ptr); void my_error_unregister_all(void); +#if !defined(O_PATH) && defined(O_EXEC) /* FreeBSD */ +#define O_PATH O_EXEC +#endif + +#ifdef O_PATH +#define HAVE_OPEN_PARENT_DIR_NOSYMLINKS +const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd); +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + int dfd, res; \ + const char *filename= my_open_parent_dir_nosymlinks(pathname, &dfd); \ + if (filename == NULL) return -1; \ + res= AT; \ + if (dfd >= 0) close(dfd); \ + return res; +#elif defined(HAVE_REALPATH) +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + char buf[PATH_MAX+1]; \ + if (realpath(pathname, buf) == NULL) return -1; \ + if (strcmp(pathname, buf)) { errno= ENOTDIR; return -1; } \ + return NOAT; +#else +#define NOSYMLINK_FUNCTION_BODY(AT,NOAT) \ + return NOAT; +#endif + +#define CREATE_NOSYMLINK_FUNCTION(PROTO,AT,NOAT) \ +static int PROTO { NOSYMLINK_FUNCTION_BODY(AT,NOAT) } + #ifdef _WIN32 #include <sys/stat.h> /* my_winfile.c exports, should not be used outside mysys */ |