support MY_NOSYMLINKS in my_delete()

This commit is contained in:
Sergei Golubchik 2017-02-20 22:41:17 +01:00
commit 6d50324558
4 changed files with 118 additions and 84 deletions

View file

@ -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)

View file

@ -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
}

View file

@ -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

View file

@ -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 */