diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2012-09-05 12:59:29 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2012-09-05 12:59:29 +0200 |
commit | e5a927883ea8f85e2258d5e15ac6ede8f44aeaab (patch) | |
tree | 1798a5dc8008c29d688093783e6bece1094cedbc /fs | |
parent | 62b665b6442c493eb2537d1c5f6c8925a2ce73bf (diff) | |
parent | 42d3d24c7152462f7bb40056255ebcfffa8dbd9f (diff) | |
download | barebox-e5a927883ea8f85e2258d5e15ac6ede8f44aeaab.tar.gz barebox-e5a927883ea8f85e2258d5e15ac6ede8f44aeaab.tar.xz |
Merge branch 'for-next/link'
Diffstat (limited to 'fs')
-rw-r--r-- | fs/fs.c | 229 | ||||
-rw-r--r-- | fs/nfs.c | 86 | ||||
-rw-r--r-- | fs/ramfs.c | 61 |
3 files changed, 335 insertions, 41 deletions
@@ -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; @@ -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, |