summaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2012-09-05 12:59:29 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2012-09-05 12:59:29 +0200
commite5a927883ea8f85e2258d5e15ac6ede8f44aeaab (patch)
tree1798a5dc8008c29d688093783e6bece1094cedbc /fs
parent62b665b6442c493eb2537d1c5f6c8925a2ce73bf (diff)
parent42d3d24c7152462f7bb40056255ebcfffa8dbd9f (diff)
downloadbarebox-e5a927883ea8f85e2258d5e15ac6ede8f44aeaab.tar.gz
barebox-e5a927883ea8f85e2258d5e15ac6ede8f44aeaab.tar.xz
Merge branch 'for-next/link'
Diffstat (limited to 'fs')
-rw-r--r--fs/fs.c229
-rw-r--r--fs/nfs.c86
-rw-r--r--fs/ramfs.c61
3 files changed, 335 insertions, 41 deletions
diff --git a/fs/fs.c b/fs/fs.c
index 8ab1a3a7ef..38917bf72b 100644
--- a/fs/fs.c
+++ b/fs/fs.c
@@ -26,6 +26,7 @@
#include <errno.h>
#include <malloc.h>
#include <linux/stat.h>
+#include <linux/err.h>
#include <fcntl.h>
#include <xfuncs.h>
#include <init.h>
@@ -111,6 +112,61 @@ static int init_cwd(void)
postcore_initcall(init_cwd);
+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);
@@ -187,6 +243,15 @@ static struct fs_device_d *get_fsdevice_by_path(const char *path)
return fs_dev_root;
}
+char *get_mounted_path(const char *path)
+{
+ struct fs_device_d *fdev;
+
+ fdev = get_fsdevice_by_path(path);
+
+ return fdev->path;
+}
+
static FILE files[MAX_FILES];
static FILE *get_file(void)
@@ -392,7 +457,7 @@ static int path_check_prereq(const char *path, unsigned int flags)
unsigned int m;
int ret = 0;
- if (stat(path, &s)) {
+ if (lstat(path, &s)) {
if (flags & S_UB_DOES_NOT_EXIST)
goto out;
ret = -ENOENT;
@@ -434,7 +499,7 @@ static int parent_check_directory(const char *path)
int ret;
char *dir = dirname(xstrdup(path));
- ret = stat(dir, &s);
+ ret = lstat(dir, &s);
free(dir);
@@ -512,18 +577,61 @@ 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;
struct fs_driver_d *fsdrv;
FILE *f;
- int exist_err;
+ int exist_err = 0;
struct stat s;
- char *path = normalise_path(pathname);
- char *freep = path;
+ char *path;
+ char *freep;
int ret;
- exist_err = stat(path, &s);
+ path = realfile(pathname, &s);
+
+ if (IS_ERR(path)) {
+ exist_err = PTR_ERR(path);
+ path = normalise_path(pathname);
+ }
+
+ freep = path;
if (!exist_err && S_ISDIR(s.st_mode)) {
ret = -EISDIR;
@@ -890,6 +998,94 @@ int close(int fd)
}
EXPORT_SYMBOL(close);
+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 *freep = p;
+ int ret;
+
+ ret = path_check_prereq(pathname, S_IFLNK);
+ if (ret)
+ goto out;
+
+ fsdev = get_fs_device_and_root_path(&p);
+ if (!fsdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ fsdrv = fsdev->driver;
+
+ if (fsdrv->readlink)
+ ret = fsdrv->readlink(&fsdev->dev, p, buf, bufsiz);
+ else
+ ret = -ENOSYS;
+
+ if (ret)
+ goto out;
+
+out:
+ free(freep);
+
+ if (ret)
+ errno = -ret;
+
+ return ret;
+}
+EXPORT_SYMBOL(readlink);
+
+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;
+ goto out;
+ }
+
+ free(freep);
+ freep = p = normalise_path(newpath);
+
+ if (!p)
+ return -ENOMEM;
+
+ ret = lstat(p, &s);
+ if (!ret) {
+ ret = -EEXIST;
+ goto out;
+ }
+
+ fsdev = get_fs_device_and_root_path(&p);
+ if (!fsdev) {
+ ret = -ENODEV;
+ goto out;
+ }
+ fsdrv = fsdev->driver;
+
+ if (fsdrv->symlink) {
+ ret = fsdrv->symlink(&fsdev->dev, pathname, p);
+ } else {
+ ret = -EPERM;
+ }
+
+out:
+ free(freep);
+ if (ret)
+ errno = -ret;
+
+ return ret;
+}
+EXPORT_SYMBOL(symlink);
+
static int fs_match(struct device_d *dev, struct driver_d *drv)
{
return strcmp(dev->name, drv->name) ? -1 : 0;
@@ -1160,6 +1356,19 @@ EXPORT_SYMBOL(closedir);
int stat(const char *filename, struct stat *s)
{
+ char *f;
+
+ f = realfile(filename, s);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ free(f);
+ return 0;
+}
+EXPORT_SYMBOL(stat);
+
+int lstat(const char *filename, struct stat *s)
+{
struct device_d *dev;
struct fs_driver_d *fsdrv;
struct fs_device_d *fsdev;
@@ -1197,7 +1406,7 @@ out:
return ret;
}
-EXPORT_SYMBOL(stat);
+EXPORT_SYMBOL(lstat);
int mkdir (const char *pathname, mode_t mode)
{
@@ -1244,6 +1453,12 @@ int rmdir (const char *pathname)
char *freep = p;
int ret;
+ ret = path_check_prereq(pathname, S_IFLNK);
+ if (!ret) {
+ ret = -ENOTDIR;
+ goto out;
+ }
+
ret = path_check_prereq(pathname, S_IFDIR | S_UB_IS_EMPTY);
if (ret)
goto out;
diff --git a/fs/nfs.c b/fs/nfs.c
index 79e667f489..4a880cd302 100644
--- a/fs/nfs.c
+++ b/fs/nfs.c
@@ -605,34 +605,6 @@ static int nfs_read_req(struct file_priv *priv, int offset, int readlen)
return 0;
}
-#if 0
-static int nfs_readlink_reply(unsigned char *pkt, unsigned len)
-{
- uint32_t *data;
- char *path;
- int rlen;
-// int ret;
-
- data = (uint32_t *)(pkt + sizeof(struct rpc_reply));
-
- data++;
-
- rlen = ntohl(net_read_uint32(data)); /* new path length */
-
- data++;
- path = (char *)data;
-
- if (*path != '/') {
- strcat(nfs_path, "/");
- strncat(nfs_path, path, rlen);
- } else {
- memcpy(nfs_path, path, rlen);
- nfs_path[rlen] = 0;
- }
- return 0;
-}
-#endif
-
static void nfs_handler(void *ctx, char *packet, unsigned len)
{
char *pkt = net_eth_to_udp_payload(packet);
@@ -742,6 +714,63 @@ static struct file_priv *nfs_do_stat(struct device_d *dev, const char *filename,
return priv;
}
+static int nfs_readlink_req(struct file_priv *priv, char* buf, size_t size)
+{
+ uint32_t data[1024];
+ uint32_t *p;
+ int len;
+ int ret;
+ char *path;
+ uint32_t *filedata;
+
+ p = &(data[0]);
+ p = rpc_add_credentials(p);
+
+ memcpy(p, priv->filefh, NFS_FHSIZE);
+ p += (NFS_FHSIZE / 4);
+
+ len = p - &(data[0]);
+
+ ret = rpc_req(priv->npriv, PROG_NFS, NFS_READLINK, data, len);
+ if (ret)
+ return ret;
+
+ filedata = nfs_packet + sizeof(struct rpc_reply);
+ filedata++;
+
+ len = ntohl(net_read_uint32(filedata)); /* new path length */
+ filedata++;
+
+ path = (char *)filedata;
+
+ if (len > size)
+ len = size;
+
+ memcpy(buf, path, len);
+
+ return 0;
+}
+
+static int nfs_readlink(struct device_d *dev, const char *filename,
+ char *realname, size_t size)
+{
+ struct file_priv *priv;
+ int ret;
+ struct stat s;
+
+ priv = nfs_do_stat(dev, filename, &s);
+ if (IS_ERR(priv))
+ return PTR_ERR(priv);
+
+ ret = nfs_readlink_req(priv, realname, size);
+ if (ret) {
+ nfs_do_close(priv);
+ return ret;
+ }
+
+ return 0;
+}
+
static int nfs_open(struct device_d *dev, FILE *file, const char *filename)
{
struct file_priv *priv;
@@ -1039,6 +1068,7 @@ static struct fs_driver_d nfs_driver = {
.rmdir = nfs_rmdir,
.write = nfs_write,
.truncate = nfs_truncate,
+ .readlink = nfs_readlink,
.flags = 0,
.drv = {
.probe = nfs_probe,
diff --git a/fs/ramfs.c b/fs/ramfs.c
index 91d06b8d3a..8f3b936685 100644
--- a/fs/ramfs.c
+++ b/fs/ramfs.c
@@ -42,6 +42,7 @@ struct ramfs_inode {
struct ramfs_inode *parent;
struct ramfs_inode *next;
struct ramfs_inode *child;
+ char *symlink;
ulong mode;
struct handle_d *handle;
@@ -176,6 +177,7 @@ static void ramfs_put_inode(struct ramfs_inode *node)
data = tmp;
}
+ free(node->symlink);
free(node->name);
free(node);
}
@@ -212,18 +214,38 @@ static struct ramfs_inode* node_insert(struct ramfs_inode *parent_node, const ch
/* ---------------------------------------------------------------*/
-static int ramfs_create(struct device_d *dev, const char *pathname, mode_t mode)
+static int __ramfs_create(struct device_d *dev, const char *pathname,
+ mode_t mode, const char *symlink)
{
struct ramfs_priv *priv = dev->priv;
struct ramfs_inode *node;
char *file;
+ char *__symlink = NULL;
node = rlookup_parent(priv, pathname, &file);
- if (node) {
- node_insert(node, file, mode);
- return 0;
+ if (!node)
+ return -ENOENT;
+
+ if (symlink) {
+ __symlink = strdup(symlink);
+ if (!__symlink)
+ return -ENOMEM;
}
- return -ENOENT;
+
+ node = node_insert(node, file, mode);
+ if (!node) {
+ free(__symlink);
+ return -ENOMEM;
+ }
+
+ node->symlink = __symlink;
+
+ return 0;
+}
+
+static int ramfs_create(struct device_d *dev, const char *pathname, mode_t mode)
+{
+ return __ramfs_create(dev, pathname, mode, NULL);
}
static int ramfs_unlink(struct device_d *dev, const char *pathname)
@@ -532,12 +554,37 @@ static int ramfs_stat(struct device_d *dev, const char *filename, struct stat *s
if (!node)
return -ENOENT;
- s->st_size = node->size;
+ s->st_size = node->symlink ? strlen(node->symlink) : node->size;
s->st_mode = node->mode;
return 0;
}
+static int ramfs_symlink(struct device_d *dev, const char *pathname,
+ const char *newpath)
+{
+ mode_t mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
+
+ return __ramfs_create(dev, newpath, mode, pathname);
+}
+
+static int ramfs_readlink(struct device_d *dev, const char *pathname,
+ char *buf, size_t bufsiz)
+{
+ struct ramfs_priv *priv = dev->priv;
+ struct ramfs_inode *node = rlookup(priv, pathname);
+ int len;
+
+ if (!node || !node->symlink)
+ return -ENOENT;
+
+ len = min(bufsiz, strlen(node->symlink));
+
+ memcpy(buf, node->symlink, len);
+
+ return 0;
+}
+
static int ramfs_probe(struct device_d *dev)
{
struct ramfs_inode *n;
@@ -584,6 +631,8 @@ static struct fs_driver_d ramfs_driver = {
.readdir = ramfs_readdir,
.closedir = ramfs_closedir,
.stat = ramfs_stat,
+ .symlink = ramfs_symlink,
+ .readlink = ramfs_readlink,
.flags = FS_DRIVER_NO_DEV,
.drv = {
.probe = ramfs_probe,