[PATCH 3/4] fs: Implement links to directories
Sascha Hauer
s.hauer at pengutronix.de
Thu May 11 02:11:00 PDT 2017
So far links can only point to files. Implement links to
directories. With this all kinds of links are supported:
- relative links
- absolute links
- links including ".."
- link loops (are detected, return -EMLINK)
Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
commands/readlink.c | 2 +-
fs/fs.c | 272 +++++++++++++++++++++++++++++-----------------------
include/fs.h | 3 +-
3 files changed, 157 insertions(+), 120 deletions(-)
diff --git a/commands/readlink.c b/commands/readlink.c
index 4ac576f16f..a19c8e0041 100644
--- a/commands/readlink.c
+++ b/commands/readlink.c
@@ -48,7 +48,7 @@ static int do_readlink(int argc, char *argv[])
goto err;
if (canonicalize) {
- char *buf = normalise_link(argv[optind], realname);
+ char *buf = canonicalize_path(realname);
if (!buf)
goto err;
diff --git a/fs/fs.c b/fs/fs.c
index 9cb15738b0..aaae5bbdd8 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -83,61 +83,6 @@ static int init_fs(void)
postcore_initcall(init_fs);
-char *normalise_link(const char *pathname, const char *symlink)
-{
- const char *buf = symlink;
- char *path_free, *path;
- char *absolute_path;
- int point = 0;
- int dir = 1;
- int len;
-
- if (symlink[0] == '/')
- return strdup(symlink);
-
- while (*buf == '.' || *buf == '/') {
- if (*buf == '.') {
- point++;
- } else if (*buf == '/') {
- point = 0;
- dir++;
- }
- if (point > 2) {
- buf -= 2;
- break;
- }
- buf++;
- }
-
- path = path_free = strdup(pathname);
- if (!path)
- return NULL;
-
- while(dir) {
- path = dirname(path);
- dir--;
- }
-
- len = strlen(buf) + strlen(path) + 1;
- if (buf[0] != '/')
- len++;
-
- absolute_path = calloc(sizeof(char), len);
-
- if (!absolute_path)
- goto out;
-
- strcat(absolute_path, path);
- if (buf[0] != '/')
- strcat(absolute_path, "/");
- strcat(absolute_path, buf);
-
-out:
- free(path_free);
-
- return absolute_path;
-}
-
char *normalise_path(const char *pathname)
{
char *path = xzalloc(strlen(pathname) + strlen(cwd) + 2);
@@ -197,6 +142,137 @@ char *normalise_path(const char *pathname)
}
EXPORT_SYMBOL(normalise_path);
+static int __lstat(const char *filename, struct stat *s);
+
+static char *__canonicalize_path(const char *_pathname, int level)
+{
+ char *path, *freep;
+ char *outpath;
+ int ret;
+ struct stat s;
+
+ if (level > 10)
+ return ERR_PTR(-ELOOP);
+
+ path = freep = xstrdup(_pathname);
+
+ if (*path == '/')
+ outpath = xstrdup("/");
+ else
+ outpath = __canonicalize_path(cwd, level + 1);
+
+ while (1) {
+ char *p = strsep(&path, "/");
+ char *tmp;
+ char link[PATH_MAX] = {};
+
+ if (!p)
+ break;
+ if (p[0] == '\0')
+ continue;
+ if (!strcmp(p, "."))
+ continue;
+ if (!strcmp(p, "..")) {
+ tmp = xstrdup(dirname(outpath));
+ free(outpath);
+ outpath = tmp;
+ continue;
+ }
+
+ tmp = basprintf("%s/%s", outpath, p);
+ free(outpath);
+ outpath = tmp;
+
+ ret = __lstat(outpath, &s);
+ if (ret)
+ goto out;
+
+ if (!S_ISLNK(s.st_mode))
+ continue;
+
+ ret = readlink(outpath, link, PATH_MAX - 1);
+ if (ret < 0)
+ goto out;
+
+ if (link[0] == '/') {
+ free(outpath);
+ outpath = __canonicalize_path(link, level + 1);
+ } else {
+ tmp = basprintf("%s/%s", dirname(outpath), link);
+ free(outpath);
+ outpath = __canonicalize_path(tmp, level + 1);
+ free(tmp);
+ }
+
+ if (IS_ERR(outpath))
+ goto out;
+ }
+out:
+ free(freep);
+
+ return outpath;
+}
+
+/*
+ * canonicalize_path - resolve links in path
+ * @pathname: The input path
+ *
+ * This function resolves all links in @pathname and returns
+ * a path without links in it.
+ *
+ * Return: Path with links resolved. Allocated, must be freed after use.
+ */
+char *canonicalize_path(const char *pathname)
+{
+ char *r, *p = __canonicalize_path(pathname, 0);
+
+ if (IS_ERR(p))
+ return ERR_CAST(p);
+
+ r = normalise_path(p);
+ free(p);
+
+ return r;
+}
+
+/*
+ * canonicalize_dir - resolve links in path
+ * @pathname: The input path
+ *
+ * This function resolves all links except the last one. Needed to give
+ * access to the link itself.
+ *
+ * Return: Path with links resolved. Allocated, must be freed after use.
+ */
+char *canonicalize_dir(const char *pathname)
+{
+ char *f, *d, *r, *ret, *p;
+ char *freep1, *freep2;
+
+ freep1 = xstrdup(pathname);
+ freep2 = xstrdup(pathname);
+ f = basename(freep1);
+ d = dirname(freep2);
+
+ p = __canonicalize_path(d, 0);
+ if (IS_ERR(p)) {
+ ret = ERR_CAST(p);
+ goto out;
+ }
+
+ r = basprintf("%s/%s", p, f);
+
+ ret = normalise_path(r);
+
+ free(r);
+ free(p);
+out:
+ free(freep1);
+ free(freep2);
+
+ return ret;
+}
+
LIST_HEAD(fs_device_list);
static struct fs_device_d *fs_dev_root;
@@ -493,7 +569,7 @@ int unlink(const char *pathname)
{
struct fs_device_d *fsdev;
struct fs_driver_d *fsdrv;
- char *p = normalise_path(pathname);
+ char *p = canonicalize_path(pathname);
char *freep = p;
int ret;
struct stat s;
@@ -530,42 +606,6 @@ out:
}
EXPORT_SYMBOL(unlink);
-static char *realfile(const char *pathname, struct stat *s)
-{
- char *path = normalise_path(pathname);
- int ret;
-
- ret = lstat(path, s);
- if (ret)
- goto out;
-
- if (S_ISLNK(s->st_mode)) {
- char tmp[PATH_MAX];
- char *new_path;
-
- memset(tmp, 0, PATH_MAX);
-
- ret = readlink(path, tmp, PATH_MAX - 1);
- if (ret < 0)
- goto out;
-
- new_path = normalise_link(path, tmp);
- free(path);
- if (!new_path)
- return ERR_PTR(-ENOMEM);
- path = new_path;
-
- ret = lstat(path, s);
- }
-
- if (!ret)
- return path;
-
-out:
- free(path);
- return ERR_PTR(ret);
-}
-
int open(const char *pathname, int flags, ...)
{
struct fs_device_d *fsdev;
@@ -577,13 +617,14 @@ int open(const char *pathname, int flags, ...)
char *freep;
int ret;
- path = realfile(pathname, &s);
-
+ path = canonicalize_path(pathname);
if (IS_ERR(path)) {
- exist_err = PTR_ERR(path);
- path = normalise_path(pathname);
+ ret = PTR_ERR(path);
+ goto out2;
}
+ exist_err = stat(path, &s);
+
freep = path;
if (!exist_err && S_ISDIR(s.st_mode)) {
@@ -657,6 +698,7 @@ out:
put_file(f);
out1:
free(freep);
+out2:
if (ret)
errno = -ret;
return ret;
@@ -1026,7 +1068,7 @@ int readlink(const char *pathname, char *buf, size_t bufsiz)
{
struct fs_driver_d *fsdrv;
struct fs_device_d *fsdev;
- char *p = normalise_path(pathname);
+ char *p = canonicalize_dir(pathname);
char *freep = p;
int ret;
struct stat s;
@@ -1070,24 +1112,15 @@ int symlink(const char *pathname, const char *newpath)
struct fs_driver_d *fsdrv;
struct fs_device_d *fsdev;
char *p;
- char *freep = normalise_path(pathname);
int ret;
struct stat s;
- if (!freep)
- return -ENOMEM;
-
- if (!stat(freep, &s) && S_ISDIR(s.st_mode)) {
- ret = -ENOSYS;
+ p = canonicalize_path(newpath);
+ if (IS_ERR(p)) {
+ ret = PTR_ERR(p);
goto out;
}
- free(freep);
- freep = p = normalise_path(newpath);
-
- if (!p)
- return -ENOMEM;
-
ret = lstat(p, &s);
if (!ret) {
ret = -EEXIST;
@@ -1108,7 +1141,7 @@ int symlink(const char *pathname, const char *newpath)
}
out:
- free(freep);
+ free(p);
if (ret)
errno = -ret;
@@ -1387,7 +1420,7 @@ DIR *opendir(const char *pathname)
DIR *dir = NULL;
struct fs_device_d *fsdev;
struct fs_driver_d *fsdrv;
- char *p = normalise_path(pathname);
+ char *p = canonicalize_path(pathname);
char *freep = p;
int ret;
struct stat s;
@@ -1467,18 +1500,21 @@ EXPORT_SYMBOL(closedir);
int stat(const char *filename, struct stat *s)
{
- char *f;
+ char *path = canonicalize_path(filename);
+ int ret;
- f = realfile(filename, s);
- if (IS_ERR(f))
- return PTR_ERR(f);
+ if (IS_ERR(path))
+ return PTR_ERR(path);
- free(f);
- return 0;
+ ret = lstat(path, s);
+
+ free(path);
+
+ return ret;
}
EXPORT_SYMBOL(stat);
-int lstat(const char *filename, struct stat *s)
+static int __lstat(const char *filename, struct stat *s)
{
struct fs_driver_d *fsdrv;
struct fs_device_d *fsdev;
diff --git a/include/fs.h b/include/fs.h
index 6a592893a9..71edb22f26 100644
--- a/include/fs.h
+++ b/include/fs.h
@@ -128,7 +128,8 @@ char *mkmodestr(unsigned long mode, char *str);
* of "..", "." and double slashes. The returned string must be freed wit free().
*/
char *normalise_path(const char *path);
-char *normalise_link(const char *pathname, const char* symlink);
+
+char *canonicalize_path(const char *pathname);
char *get_mounted_path(const char *path);
--
2.11.0
More information about the barebox
mailing list