summaryrefslogtreecommitdiff
path: root/init/do_mounts_devfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'init/do_mounts_devfs.c')
-rw-r--r--init/do_mounts_devfs.c137
1 files changed, 137 insertions, 0 deletions
diff --git a/init/do_mounts_devfs.c b/init/do_mounts_devfs.c
new file mode 100644
index 000000000000..cc526474690a
--- /dev/null
+++ b/init/do_mounts_devfs.c
@@ -0,0 +1,137 @@
+
+#include <linux/kernel.h>
+#include <linux/dirent.h>
+#include <linux/string.h>
+
+#include "do_mounts.h"
+
+void __init mount_devfs(void)
+{
+ sys_mount("devfs", "/dev", "devfs", 0, NULL);
+}
+
+void __init umount_devfs(char *path)
+{
+ sys_umount(path, 0);
+}
+
+/*
+ * If the dir will fit in *buf, return its length. If it won't fit, return
+ * zero. Return -ve on error.
+ */
+static int __init do_read_dir(int fd, void *buf, int len)
+{
+ long bytes, n;
+ char *p = buf;
+ sys_lseek(fd, 0, 0);
+
+ for (bytes = 0; bytes < len; bytes += n) {
+ n = sys_getdents64(fd, (struct linux_dirent64 *)(p + bytes),
+ len - bytes);
+ if (n < 0)
+ return n;
+ if (n == 0)
+ return bytes;
+ }
+ return 0;
+}
+
+/*
+ * Try to read all of a directory. Returns the contents at *p, which
+ * is kmalloced memory. Returns the number of bytes read at *len. Returns
+ * NULL on error.
+ */
+static void * __init read_dir(char *path, int *len)
+{
+ int size;
+ int fd = sys_open(path, 0, 0);
+
+ *len = 0;
+ if (fd < 0)
+ return NULL;
+
+ for (size = 1 << 9; size <= (PAGE_SIZE << MAX_ORDER); size <<= 1) {
+ void *p = kmalloc(size, GFP_KERNEL);
+ int n;
+ if (!p)
+ break;
+ n = do_read_dir(fd, p, size);
+ if (n > 0) {
+ sys_close(fd);
+ *len = n;
+ return p;
+ }
+ kfree(p);
+ if (n == -EINVAL)
+ continue; /* Try a larger buffer */
+ if (n < 0)
+ break;
+ }
+ sys_close(fd);
+ return NULL;
+}
+
+/*
+ * recursively scan <path>, looking for a device node of type <dev>
+ */
+static int __init find_in_devfs(char *path, unsigned dev)
+{
+ char *end = path + strlen(path);
+ int rest = path + 64 - end;
+ int size;
+ char *p = read_dir(path, &size);
+ char *s;
+
+ if (!p)
+ return -1;
+ for (s = p; s < p + size; s += ((struct linux_dirent64 *)s)->d_reclen) {
+ struct linux_dirent64 *d = (struct linux_dirent64 *)s;
+ if (strlen(d->d_name) + 2 > rest)
+ continue;
+ switch (d->d_type) {
+ case DT_BLK:
+ sprintf(end, "/%s", d->d_name);
+ if (bstat(path) != dev)
+ break;
+ kfree(p);
+ return 0;
+ case DT_DIR:
+ if (strcmp(d->d_name, ".") == 0)
+ break;
+ if (strcmp(d->d_name, "..") == 0)
+ break;
+ sprintf(end, "/%s", d->d_name);
+ if (find_in_devfs(path, dev) < 0)
+ break;
+ kfree(p);
+ return 0;
+ }
+ }
+ kfree(p);
+ return -1;
+}
+
+/*
+ * create a device node called <name> which points to
+ * <devfs_name> if possible, otherwise find a device node
+ * which matches <dev> and make <name> a symlink pointing to it.
+ */
+int __init create_dev(char *name, dev_t dev, char *devfs_name)
+{
+ char path[64];
+
+ sys_unlink(name);
+ if (devfs_name && devfs_name[0]) {
+ if (strncmp(devfs_name, "/dev/", 5) == 0)
+ devfs_name += 5;
+ sprintf(path, "/dev/%s", devfs_name);
+ if (sys_access(path, 0) == 0)
+ return sys_symlink(devfs_name, name);
+ }
+ if (!dev)
+ return -1;
+ strcpy(path, "/dev");
+ if (find_in_devfs(path, new_encode_dev(dev)) < 0)
+ return -1;
+ return sys_symlink(path + 5, name);
+}