diff --git a/libc/Makefile b/libc/Makefile
index e02c8ba1..b0431658 100644
--- a/libc/Makefile
+++ b/libc/Makefile
@@ -125,6 +125,8 @@ wctype.o \
HOSTEDOBJS=\
access.o \
calltrace.o \
+canonicalize_file_name_at.o \
+canonicalize_file_name.o \
chdir.o \
chmod.o \
chown.o \
diff --git a/libc/canonicalize_file_name.cpp b/libc/canonicalize_file_name.cpp
new file mode 100644
index 00000000..0a87a5b2
--- /dev/null
+++ b/libc/canonicalize_file_name.cpp
@@ -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 .
+
+ canonicalize_file_name.cpp
+ Return the canonicalized filename.
+
+*******************************************************************************/
+
+#include
+#include
+
+extern "C" char* canonicalize_file_name(const char* path)
+{
+ return canonicalize_file_name_at(AT_FDCWD, path);
+}
diff --git a/libc/canonicalize_file_name_at.cpp b/libc/canonicalize_file_name_at.cpp
new file mode 100644
index 00000000..0bb3ffb4
--- /dev/null
+++ b/libc/canonicalize_file_name_at.cpp
@@ -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 .
+
+ canonicalize_file_name_at.cpp
+ Return the canonicalized filename.
+
+*******************************************************************************/
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+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;
+}
diff --git a/libc/getcwd.cpp b/libc/getcwd.cpp
index 0670bd90..203bb4c6 100644
--- a/libc/getcwd.cpp
+++ b/libc/getcwd.cpp
@@ -22,105 +22,14 @@
*******************************************************************************/
-#include
-#include
-#include
#include
-#include
#include
#include
#include
-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)
{
- int fd;
- 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;
+ return canonicalize_file_name(".");
}
extern "C" char* getcwd(char* buf, size_t size)
@@ -130,7 +39,12 @@ extern "C" char* getcwd(char* buf, size_t size)
return cwd;
if ( !cwd )
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);
free(cwd);
return buf;
diff --git a/libc/include/stdlib.h b/libc/include/stdlib.h
index a50a8fc0..6893686f 100644
--- a/libc/include/stdlib.h
+++ b/libc/include/stdlib.h
@@ -69,6 +69,8 @@ long atol(const char*);
long long atoll(const char*);
void* bsearch(const void*, const void*, size_t, size_t, int (*)(const void*, const void*));
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);
void exit(int) __attribute__ ((__noreturn__));
void _Exit(int status) __attribute__ ((__noreturn__));