Add canonicalize_file_name{,_at}(3).
This commit is contained in:
parent
1a3aa45c2c
commit
0ce3d61cb9
|
@ -125,6 +125,8 @@ wctype.o \
|
||||||
HOSTEDOBJS=\
|
HOSTEDOBJS=\
|
||||||
access.o \
|
access.o \
|
||||||
calltrace.o \
|
calltrace.o \
|
||||||
|
canonicalize_file_name_at.o \
|
||||||
|
canonicalize_file_name.o \
|
||||||
chdir.o \
|
chdir.o \
|
||||||
chmod.o \
|
chmod.o \
|
||||||
chown.o \
|
chown.o \
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
canonicalize_file_name.cpp
|
||||||
|
Return the canonicalized filename.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern "C" char* canonicalize_file_name(const char* path)
|
||||||
|
{
|
||||||
|
return canonicalize_file_name_at(AT_FDCWD, path);
|
||||||
|
}
|
|
@ -0,0 +1,206 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
Copyright(C) Jonas 'Sortie' Termansen 2013.
|
||||||
|
|
||||||
|
This file is part of the Sortix C Library.
|
||||||
|
|
||||||
|
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
option) any later version.
|
||||||
|
|
||||||
|
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||||
|
License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License
|
||||||
|
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
canonicalize_file_name_at.cpp
|
||||||
|
Return the canonicalized filename.
|
||||||
|
|
||||||
|
*******************************************************************************/
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static int dup_handles_cwd(int fd)
|
||||||
|
{
|
||||||
|
if ( fd == AT_FDCWD )
|
||||||
|
return open(".", O_RDONLY | O_DIRECTORY);
|
||||||
|
return dup(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* FindDirectoryEntryAt(int dirfd, ino_t inode, dev_t dev)
|
||||||
|
{
|
||||||
|
int dupdirfd = dup_handles_cwd(dirfd);
|
||||||
|
if ( dupdirfd < 0 )
|
||||||
|
return NULL;
|
||||||
|
DIR* dir = fdopendir(dupdirfd);
|
||||||
|
if ( !dir ) { close(dupdirfd); return NULL; }
|
||||||
|
struct dirent* entry;
|
||||||
|
while ( (entry = readdir(dir)) )
|
||||||
|
{
|
||||||
|
if ( !strcmp(entry->d_name, "..") )
|
||||||
|
continue;
|
||||||
|
struct stat st;
|
||||||
|
if ( fstatat(dupdirfd, entry->d_name, &st, AT_SYMLINK_NOFOLLOW) )
|
||||||
|
continue; // Not ideal, but missing permissions, broken symlinks..
|
||||||
|
if ( st.st_ino == inode && st.st_dev == dev )
|
||||||
|
{
|
||||||
|
char* result = strdup(entry->d_name);
|
||||||
|
closedir(dir);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
closedir(dir);
|
||||||
|
errno = ENOENT;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* SkipSlashes(const char* path)
|
||||||
|
{
|
||||||
|
while ( *path == '/' )
|
||||||
|
path++;
|
||||||
|
return *path ? path : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" char* canonicalize_file_name_at(int dirfd, const char* path)
|
||||||
|
{
|
||||||
|
if ( path && path[0] == '.' && !path[1] )
|
||||||
|
path = NULL;
|
||||||
|
|
||||||
|
// The below code is able to determine the absolute path to a directory by
|
||||||
|
// using the .. entry and searching the parent directory. This doesn't work
|
||||||
|
// for files, as they can be linked in any number of directories and have no
|
||||||
|
// pointer back to the directory they were opened from. We'll therefore
|
||||||
|
// follow the path until the last element, and if the path points to a file
|
||||||
|
// we'll simply append that to the final result.
|
||||||
|
if ( path )
|
||||||
|
{
|
||||||
|
// Open the directory specified by the last slash in the path.
|
||||||
|
char* last_slash = strrchr(path, '/');
|
||||||
|
if ( last_slash )
|
||||||
|
{
|
||||||
|
size_t subpath_len = (size_t) (last_slash - path + 1);
|
||||||
|
char* subpath = (char*) malloc((subpath_len + 1) * sizeof(char));
|
||||||
|
memcpy(subpath, path, subpath_len * sizeof(char));
|
||||||
|
subpath[subpath_len] = '\0';
|
||||||
|
int fd = openat(dirfd, subpath, O_RDONLY | O_DIRECTORY);
|
||||||
|
free(subpath);
|
||||||
|
if ( fd < 0 )
|
||||||
|
return NULL;
|
||||||
|
path = SkipSlashes(last_slash + 1);
|
||||||
|
char* ret = canonicalize_file_name_at(fd, path);
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have reached the final element in the path. It could be:
|
||||||
|
// 1) A directory.
|
||||||
|
// 2) A file.
|
||||||
|
// 3) A symbolic link to a directory.
|
||||||
|
// 4) A symbolic link to a file.
|
||||||
|
|
||||||
|
// The ideal case is if it's a directory (case 1). We follow symbolic
|
||||||
|
// links to directories as that's also okay (case 3).
|
||||||
|
int fd = openat(dirfd, path, O_RDONLY | O_DIRECTORY);
|
||||||
|
if ( fd )
|
||||||
|
{
|
||||||
|
char* ret = canonicalize_file_name_at(fd, NULL);
|
||||||
|
close(fd);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop if an error happened other that the target wasn't a directory.
|
||||||
|
if ( errno != ENOTDIR )
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// TODO: Symbolic link support here.
|
||||||
|
// Okay, so now we are dealing with either case 2 or case 4. Since
|
||||||
|
// Sortix doesn't have current have symbolic links, we'll be lazy and
|
||||||
|
// just assume we are dealing with a file. Otherwise, we'd have to open
|
||||||
|
// with O_NOFOLLOW and if that fails, use readlink to figure out where
|
||||||
|
// the symbolic link is going, open the directory that contains it and
|
||||||
|
// continue this function from there - or something.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Our task is now to determine the absolute path of the directory opened as
|
||||||
|
// dirfd and append the path variable to it, if any. We'll find the inode
|
||||||
|
// information of the directory and search its parent directory for an entry
|
||||||
|
// that resolves to this directory. That'll give us one part of the path.
|
||||||
|
// We'll then apply that techinique recursively until we hit a directory
|
||||||
|
// whose parent is itself (the root directory).
|
||||||
|
|
||||||
|
int fd;
|
||||||
|
int parent;
|
||||||
|
struct stat fdst;
|
||||||
|
struct stat parentst;
|
||||||
|
size_t retlen = 0;
|
||||||
|
size_t newretlen;
|
||||||
|
char* ret = NULL;
|
||||||
|
char* newret;
|
||||||
|
char* elem;
|
||||||
|
|
||||||
|
if ( path )
|
||||||
|
{
|
||||||
|
if ( !(ret = (char*) malloc(sizeof(char) * (1 + strlen(path) + 1))) )
|
||||||
|
return NULL;
|
||||||
|
stpcpy(stpcpy(ret, "/"), path);
|
||||||
|
}
|
||||||
|
if ( (fd = dup_handles_cwd(dirfd)) < 0 )
|
||||||
|
goto cleanup_done;
|
||||||
|
if ( fstat(fd, &fdst) )
|
||||||
|
goto cleanup_fd;
|
||||||
|
if ( !S_ISDIR(fdst.st_mode) )
|
||||||
|
{
|
||||||
|
errno = ENOTDIR;
|
||||||
|
goto cleanup_fd;
|
||||||
|
}
|
||||||
|
next_parent:
|
||||||
|
parent = openat(fd, "..", O_RDONLY | O_DIRECTORY);
|
||||||
|
if ( parent < 0 )
|
||||||
|
goto cleanup_fd;
|
||||||
|
if ( fstat(parent, &parentst) )
|
||||||
|
goto cleanup_parent;
|
||||||
|
if ( fdst.st_ino == parentst.st_ino &&
|
||||||
|
fdst.st_dev == parentst.st_dev )
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
close(parent);
|
||||||
|
return ret ? ret : strdup("/");
|
||||||
|
}
|
||||||
|
elem = FindDirectoryEntryAt(parent, fdst.st_ino, fdst.st_dev);
|
||||||
|
if ( !elem )
|
||||||
|
goto cleanup_parent;
|
||||||
|
newretlen = 1 + strlen(elem) + retlen;
|
||||||
|
newret = (char*) malloc(sizeof(char) * (newretlen + 1));
|
||||||
|
if ( !newret )
|
||||||
|
goto cleanup_elem;
|
||||||
|
stpcpy(stpcpy(stpcpy(newret, "/"), elem), ret ? ret : "");
|
||||||
|
free(elem);
|
||||||
|
free(ret);
|
||||||
|
ret = newret;
|
||||||
|
retlen = newretlen;
|
||||||
|
close(fd);
|
||||||
|
fd = parent;
|
||||||
|
fdst = parentst;
|
||||||
|
goto next_parent;
|
||||||
|
|
||||||
|
cleanup_elem:
|
||||||
|
free(elem);
|
||||||
|
cleanup_parent:
|
||||||
|
close(parent);
|
||||||
|
cleanup_fd:
|
||||||
|
close(fd);
|
||||||
|
cleanup_done:
|
||||||
|
free(ret);
|
||||||
|
return NULL;
|
||||||
|
}
|
100
libc/getcwd.cpp
100
libc/getcwd.cpp
|
@ -22,105 +22,14 @@
|
||||||
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
static int dup_handles_cwd(int fd)
|
|
||||||
{
|
|
||||||
if ( fd == AT_FDCWD )
|
|
||||||
return open(".", O_RDONLY | O_DIRECTORY);
|
|
||||||
return dup(fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* FindDirectoryEntryAt(int dirfd, ino_t inode, dev_t dev)
|
|
||||||
{
|
|
||||||
int dupdirfd = dup_handles_cwd(dirfd);
|
|
||||||
if ( dupdirfd < 0 )
|
|
||||||
return NULL;
|
|
||||||
DIR* dir = fdopendir(dupdirfd);
|
|
||||||
if ( !dir ) { close(dupdirfd); return NULL; }
|
|
||||||
struct dirent* entry;
|
|
||||||
while ( (entry = readdir(dir)) )
|
|
||||||
{
|
|
||||||
if ( !strcmp(entry->d_name, "..") )
|
|
||||||
continue;
|
|
||||||
struct stat st;
|
|
||||||
if ( fstatat(dupdirfd, entry->d_name, &st, 0) )
|
|
||||||
continue; // Not ideal, but missing permissions, broken symlinks..
|
|
||||||
if ( st.st_ino == inode && st.st_dev == dev )
|
|
||||||
{
|
|
||||||
char* result = strdup(entry->d_name);
|
|
||||||
closedir(dir);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
closedir(dir);
|
|
||||||
errno = ENOENT;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" char* get_current_dir_name(void)
|
extern "C" char* get_current_dir_name(void)
|
||||||
{
|
{
|
||||||
int fd;
|
return canonicalize_file_name(".");
|
||||||
int parent;
|
|
||||||
struct stat fdst;
|
|
||||||
struct stat parentst;
|
|
||||||
size_t retlen = 0;
|
|
||||||
size_t newretlen;
|
|
||||||
char* ret = NULL;
|
|
||||||
char* newret;
|
|
||||||
char* elem;
|
|
||||||
|
|
||||||
fd = open(".", O_RDONLY | O_DIRECTORY);
|
|
||||||
if ( fd < 0 )
|
|
||||||
goto cleanup_done;
|
|
||||||
if ( fstat(fd, &fdst) )
|
|
||||||
goto cleanup_fd;
|
|
||||||
next_parent:
|
|
||||||
parent = openat(fd, "..", O_RDONLY | O_DIRECTORY);
|
|
||||||
if ( parent < 0 )
|
|
||||||
goto cleanup_fd;
|
|
||||||
if ( fstat(parent, &parentst) )
|
|
||||||
goto cleanup_parent;
|
|
||||||
if ( fdst.st_ino == parentst.st_ino &&
|
|
||||||
fdst.st_dev == parentst.st_dev )
|
|
||||||
{
|
|
||||||
close(fd);
|
|
||||||
close(parent);
|
|
||||||
return ret ? ret : strdup("/");
|
|
||||||
}
|
|
||||||
elem = FindDirectoryEntryAt(parent, fdst.st_ino, fdst.st_dev);
|
|
||||||
if ( !elem )
|
|
||||||
goto cleanup_parent;
|
|
||||||
newretlen = 1 + strlen(elem) + retlen;
|
|
||||||
newret = (char*) malloc(sizeof(char) * (newretlen + 1));
|
|
||||||
if ( !newret )
|
|
||||||
goto cleanup_elem;
|
|
||||||
stpcpy(stpcpy(stpcpy(newret, "/"), elem), ret ? ret : "");
|
|
||||||
free(elem);
|
|
||||||
free(ret);
|
|
||||||
ret = newret;
|
|
||||||
retlen = newretlen;
|
|
||||||
close(fd);
|
|
||||||
fd = parent;
|
|
||||||
fdst = parentst;
|
|
||||||
goto next_parent;
|
|
||||||
|
|
||||||
cleanup_elem:
|
|
||||||
free(elem);
|
|
||||||
cleanup_parent:
|
|
||||||
close(parent);
|
|
||||||
cleanup_fd:
|
|
||||||
close(fd);
|
|
||||||
cleanup_done:
|
|
||||||
free(ret);
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" char* getcwd(char* buf, size_t size)
|
extern "C" char* getcwd(char* buf, size_t size)
|
||||||
|
@ -130,7 +39,12 @@ extern "C" char* getcwd(char* buf, size_t size)
|
||||||
return cwd;
|
return cwd;
|
||||||
if ( !cwd )
|
if ( !cwd )
|
||||||
return NULL;
|
return NULL;
|
||||||
if ( size < strlen(cwd)+1 ) { free(cwd); errno = ERANGE; return NULL; }
|
if ( size < strlen(cwd) + 1 )
|
||||||
|
{
|
||||||
|
free(cwd);
|
||||||
|
errno = ERANGE;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
strcpy(buf, cwd);
|
strcpy(buf, cwd);
|
||||||
free(cwd);
|
free(cwd);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
@ -69,6 +69,8 @@ long atol(const char*);
|
||||||
long long atoll(const char*);
|
long long atoll(const char*);
|
||||||
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
|
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
|
||||||
void* calloc(size_t, size_t);
|
void* calloc(size_t, size_t);
|
||||||
|
char* canonicalize_file_name(const char* path);
|
||||||
|
char* canonicalize_file_name_at(int dirfd, const char* path);
|
||||||
div_t div(int, int);
|
div_t div(int, int);
|
||||||
void exit(int) __attribute__ ((__noreturn__));
|
void exit(int) __attribute__ ((__noreturn__));
|
||||||
void _Exit(int status) __attribute__ ((__noreturn__));
|
void _Exit(int status) __attribute__ ((__noreturn__));
|
||||||
|
|
Loading…
Reference in New Issue