summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2017-05-09 07:38:48 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2017-05-11 21:31:36 +0200
commita602bebcf7e42eb8933f224c12cad1c9320e28a1 (patch)
treef0ea6b74bf9e9dcf7f103c4bb9173b5dab5ffabc /fs
parent7f01c05e1e7e2cc1d9c389bd664b1b6eeaaa8ba6 (diff)
downloadbarebox-a602bebcf7e42eb8933f224c12cad1c9320e28a1.tar.gz
barebox-a602bebcf7e42eb8933f224c12cad1c9320e28a1.tar.xz
fs: Implement links to directories
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@pengutronix.de>
Diffstat (limited to 'fs')
-rw-r--r--fs/fs.c287
1 files changed, 169 insertions, 118 deletions
diff --git a/fs/fs.c b/fs/fs.c
index 9cb15738b0..714dc832a5 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;
@@ -1515,6 +1551,21 @@ out:
return ret;
}
+
+int lstat(const char *filename, struct stat *s)
+{
+ char *f = canonicalize_dir(filename);
+ int ret;
+
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ ret = __lstat(f, s);
+
+ free(f);
+
+ return ret;
+}
EXPORT_SYMBOL(lstat);
int fstat(int fd, struct stat *s)