SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
ADD_LIBRARY(fstools SHARED
+ libfstools/common.c
libfstools/snapshot.c
libfstools/extroot.c
libfstools/overlay.c
libfstools/mount.c
libfstools/ubi.c
libfstools/rootdisk.c
+ libfstools/partname.c
libfstools/find.c)
TARGET_LINK_LIBRARIES(fstools ubox)
INSTALL(TARGETS fstools LIBRARY DESTINATION lib)
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common.h"
+#define BUFLEN 128
+
+int
+read_uint_from_file(char *dirname, char *filename, unsigned int *i)
+{
+ FILE *f;
+ char fname[BUFLEN];
+ int ret = -1;
+
+ snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
+
+ f = fopen(fname, "r");
+ if (!f)
+ return ret;
+
+ if (fscanf(f, "%u", i) == 1)
+ ret = 0;
+
+ fclose(f);
+ return ret;
+}
+
+char
+*read_string_from_file(const char *dirname, const char *filename, char *buf, size_t bufsz)
+{
+ FILE *f;
+ char fname[BUFLEN];
+ int i;
+
+ snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
+
+ f = fopen(fname, "r");
+ if (!f)
+ return NULL;
+
+ if (fgets(buf, bufsz, f) == NULL)
+ return NULL;
+
+ fclose(f);
+
+ /* make sure the string is \0 terminated */
+ buf[bufsz - 1] = '\0';
+
+ /* remove trailing whitespace */
+ i = strlen(buf) - 1;
+ while (i > 0 && buf[i] <= ' ')
+ buf[i--] = '\0';
+
+ return buf;
+}
+
+int block_file_identify(FILE *f, uint64_t offset)
+{
+ uint32_t magic = 0;
+ size_t n;
+
+ fseeko(f, offset, SEEK_SET);
+ n = fread(&magic, sizeof(magic), 1, f);
+ if (magic == cpu_to_le32(0x88b1f)) {
+ return FS_TARGZ;
+ }
+
+ fseeko(f, offset + 0x400, SEEK_SET);
+ n = fread(&magic, sizeof(magic), 1, f);
+ if (n != 1)
+ return -1;
+
+ if (magic == cpu_to_le32(0xF2F52010))
+ return FS_F2FS;
+
+ magic = 0;
+ fseeko(f, offset + 0x438, SEEK_SET);
+ n = fread(&magic, sizeof(magic), 1, f);
+ if (n != 1)
+ return -1;
+
+ if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
+ return FS_EXT4;
+
+ return FS_NONE;
+}
+
+static bool use_f2fs(struct volume *v, uint64_t offset, const char *bdev)
+{
+ uint64_t size = 0;
+ bool ret = false;
+ int fd;
+
+ fd = open(bdev, O_RDONLY);
+ if (ioctl(fd, BLKGETSIZE64, &size) == 0)
+ ret = size - offset > F2FS_MINSIZE;
+ close(fd);
+
+ return ret;
+}
+
+int block_volume_format(struct volume *v, uint64_t offset, const char *bdev)
+{
+ int ret = 0;
+ char str[128];
+
+ switch (volume_identify(v)) {
+ case FS_TARGZ:
+ snprintf(str, sizeof(str), "gzip -cd %s > /tmp/sysupgrade.tar", v->blk);
+ system(str);
+ /* fall-through */
+ case FS_NONE:
+ ULOG_INFO("overlay filesystem in %s has not been formatted yet\n", v->blk);
+ if (use_f2fs(v, offset, bdev))
+ snprintf(str, sizeof(str), "mkfs.f2fs -q -l rootfs_data %s", v->blk);
+ else
+ snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk);
+
+ ret = system(str);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#define _FILE_OFFSET_BITS 64
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <glob.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <libgen.h>
+
+#include "libfstools.h"
+#include "volume.h"
+
+#define F2FS_MINSIZE (100ULL * 1024ULL * 1024ULL)
+
+int read_uint_from_file(char *dirname, char *filename, unsigned int *i);
+char *read_string_from_file(const char *dirname, const char *filename, char *buf, size_t bufsz);
+int block_file_identify(FILE *f, uint64_t offset);
+int block_volume_format(struct volume *v, uint64_t offset, const char *bdev);
FS_UBIFS,
FS_F2FS,
FS_EXT4,
+ FS_TARGZ,
};
enum fs_state {
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "common.h"
+
+#define BUFLEN 64
+
+const char *const block_dir_name = "/sys/class/block";
+
+struct partname_volume {
+ struct volume v;
+ char devname[16];
+ char parent_devname[16];
+};
+
+static struct driver partname_driver;
+
+static int partname_volume_identify(struct volume *v)
+{
+ int ret = FS_NONE;
+ FILE *f;
+
+ f = fopen(v->blk, "r");
+ if (!f)
+ return ret;
+
+ ret = block_file_identify(f, 0);
+
+ fclose(f);
+
+ return ret;
+}
+
+static int partname_volume_init(struct volume *v)
+{
+ struct partname_volume *p = container_of(v, struct partname_volume, v);
+ char voldir[BUFLEN];
+ char voldev[BUFLEN];
+ char pdev[BUFLEN];
+ unsigned int volsize;
+
+ snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->devname);
+
+ if (read_uint_from_file(voldir, "size", &volsize))
+ return -1;
+
+ snprintf(voldev, sizeof(voldev), "/dev/%s", p->devname);
+ snprintf(pdev, sizeof(pdev), "/dev/%s", p->parent_devname);
+
+ v->type = BLOCKDEV;
+ v->size = volsize << 9; /* size is returned in sectors of 512 bytes */
+ v->blk = strdup(voldev);
+
+ return block_volume_format(v, 0, pdev);
+}
+
+/* from procd/utils.c -> should go to libubox */
+static char* get_cmdline_val(const char* name, char* out, int len)
+{
+ char line[1024], *c, *sptr;
+ int fd = open("/proc/cmdline", O_RDONLY);
+ ssize_t r = read(fd, line, sizeof(line) - 1);
+ close(fd);
+
+ if (r <= 0)
+ return NULL;
+
+ line[r] = 0;
+
+ for (c = strtok_r(line, " \t\n", &sptr); c;
+ c = strtok_r(NULL, " \t\n", &sptr)) {
+ char *sep = strchr(c, '=');
+ if (sep == NULL)
+ continue;
+
+ ssize_t klen = sep - c;
+ if (strncmp(name, c, klen) || name[klen] != 0)
+ continue;
+
+ strncpy(out, &sep[1], len);
+ out[len-1] = 0;
+ return out;
+ }
+
+ return NULL;
+}
+
+static char *rootdevname(char *devpath) {
+ int l;
+
+ l = strlen(devpath) - 1;
+
+ /* strip partition suffix from root=/dev/... string */
+ while (l > 0 && (devpath[l] >= '0' && devpath[l] <= '9'))
+ --l;
+
+ if (devpath[l] != 'p')
+ ++l;
+
+ devpath[l] = '\0';
+
+ return basename(devpath);
+}
+
+static struct volume *partname_volume_find(char *name)
+{
+ struct partname_volume *p;
+ char volnamegstr[BUFLEN];
+ char namebuf[BUFLEN];
+ char rootparam[BUFLEN];
+ char *rootdev = NULL, *devname, *tmp;
+ int j;
+ bool found = false;
+ glob_t gl;
+
+ if (get_cmdline_val("root", rootparam, sizeof(rootparam))) {
+ rootdev = rootdevname(rootparam);
+ /* find partition on same device as rootfs */
+ snprintf(volnamegstr, sizeof(volnamegstr), "%s/%s/*/name", block_dir_name, rootdev);
+ } else {
+ /* no 'root=' kernel cmdline parameter, find on any block device */
+ snprintf(volnamegstr, sizeof(volnamegstr), "%s/*/name", block_dir_name);
+ }
+
+ if (!glob(volnamegstr, GLOB_NOESCAPE, NULL, &gl))
+ for (j = 0; j < gl.gl_pathc; j++) {
+ if (!read_string_from_file("", gl.gl_pathv[j], namebuf, sizeof(namebuf)))
+ continue;
+ if (!strncmp(namebuf, name, sizeof(namebuf))) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ return NULL;
+
+ devname = gl.gl_pathv[j];
+ tmp = strrchr(devname, '/');
+ *tmp = '\0';
+ devname = strrchr(devname, '/') + 1;
+
+ p = calloc(1, sizeof(*p));
+ strncpy(p->devname, devname, sizeof(p->devname));
+ if (rootdev)
+ strncpy(p->parent_devname, rootdev, sizeof(p->devname));
+ else
+ strncpy(p->parent_devname, rootdevname(devname), sizeof(p->devname));
+
+ p->devname[sizeof(p->devname)-1] = '\0';
+ p->v.drv = &partname_driver;
+ p->v.name = strdup(namebuf);
+
+ return &p->v;
+}
+
+static struct driver partname_driver = {
+ .name = "partname",
+ .find = partname_volume_find,
+ .init = partname_volume_init,
+ .identify = partname_volume_identify,
+};
+
+DRIVER(partname_driver);
* GNU General Public License for more details.
*/
-#define _FILE_OFFSET_BITS 64
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-#include <sys/mount.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#include "libfstools.h"
-#include "volume.h"
+#include "common.h"
#include <linux/loop.h>
return 0;
}
-static bool rootdisk_use_f2fs(struct rootdev_volume *p)
-{
- uint64_t size = 0;
- bool ret = false;
- int fd;
-
- fd = open(rootdev, O_RDONLY);
- if (ioctl(fd, BLKGETSIZE64, &size) == 0)
- ret = size - p->offset > F2FS_MINSIZE;
- close(fd);
-
- return ret;
-}
-
static struct volume *rootdisk_volume_find(char *name)
{
struct squashfs_super_block sb;
static int rootdisk_volume_identify(struct volume *v)
{
struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
- int ret = FS_NONE;
- uint32_t magic = 0;
- size_t n;
FILE *f;
-
+ int ret = FS_NONE;
f = fopen(rootdev, "r");
if (!f)
return ret;
- fseeko(f, p->offset + 0x400, SEEK_SET);
- n = fread(&magic, sizeof(magic), 1, f);
- if (n != 1)
- return -1;
-
- if (magic == cpu_to_le32(0xF2F52010))
- ret = FS_F2FS;
-
- magic = 0;
- fseeko(f, p->offset + 0x438, SEEK_SET);
- n = fread(&magic, sizeof(magic), 1, f);
- if (n != 1)
- return -1;
- if ((le32_to_cpu(magic) & 0xffff) == 0xef53)
- ret = FS_EXT4;
+ ret = block_file_identify(f, p->offset);
fclose(f);
static int rootdisk_volume_init(struct volume *v)
{
struct rootdev_volume *p = container_of(v, struct rootdev_volume, v);
- char str[128];
- int ret = 0;
if (!p->loop_name[0] && rootdisk_create_loop(p) != 0) {
ULOG_ERR("unable to create loop device\n");
v->type = BLOCKDEV;
v->blk = p->loop_name;
- switch (rootdisk_volume_identify(v)) {
- case FS_NONE:
- ULOG_INFO("rootdisk overlay filesystem has not been formatted yet\n");
- if (rootdisk_use_f2fs(p))
- snprintf(str, sizeof(str), "mkfs.f2fs -q -l rootfs_data %s", v->blk);
- else
- snprintf(str, sizeof(str), "mkfs.ext4 -q -L rootfs_data %s", v->blk);
- ret = system(str);
- break;
- default:
- break;
- }
- return ret;
+ return block_volume_format(v, p->offset, rootdev);
}
static struct driver rootdisk_driver = {
* GNU General Public License for more details.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <dirent.h>
-
-#include "libfstools.h"
-#include "volume.h"
+#include "common.h"
/* fit for UBI_MAX_VOLUME_NAME and sysfs path lengths */
#define BUFLEN 128
static struct driver ubi_driver;
-static int
-read_uint_from_file(char *dirname, char *filename, unsigned int *i)
-{
- FILE *f;
- char fname[BUFLEN];
- int ret = -1;
-
- snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
-
- f = fopen(fname, "r");
- if (!f)
- return ret;
-
- if (fscanf(f, "%u", i) == 1)
- ret = 0;
-
- fclose(f);
- return ret;
-}
-
-static char
-*read_string_from_file(char *dirname, char *filename)
-{
- FILE *f;
- char fname[BUFLEN];
- char buf[BUFLEN];
- int i;
-
- snprintf(fname, sizeof(fname), "%s/%s", dirname, filename);
-
- f = fopen(fname, "r");
- if (!f)
- return NULL;
-
- if (fgets(buf, sizeof(buf), f) == NULL)
- return NULL;
-
- fclose(f);
-
- /* make sure the string is \0 terminated */
- buf[sizeof(buf) - 1] = '\0';
-
- /* remove trailing whitespace */
- i = strlen(buf) - 1;
- while (i > 0 && buf[i] <= ' ')
- buf[i--] = '\0';
-
- return strdup(buf);
-}
-
static unsigned int
test_open(char *filename)
{
static int ubi_volume_init(struct volume *v)
{
struct ubi_volume *p = container_of(v, struct ubi_volume, v);
- char voldir[BUFLEN], voldev[BUFLEN], *volname;
+ char voldir[BUFLEN], voldev[BUFLEN], volname[BUFLEN];
unsigned int volsize;
snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
snprintf(voldev, sizeof(voldev), "/dev/ubi%u_%u",
p->ubi_num, p->ubi_volid);
- volname = read_string_from_file(voldir, "name");
- if (!volname)
+ if (!read_string_from_file(voldir, "name", volname, sizeof(volname)))
return -1;
if (read_uint_from_file(voldir, "data_bytes", &volsize))
static struct volume *ubi_volume_match(char *name, int ubi_num, int volid)
{
- char voldir[BUFLEN], volblkdev[BUFLEN], *volname;
+ char voldir[BUFLEN], volblkdev[BUFLEN], volname[BUFLEN];
struct ubi_volume *p;
snprintf(voldir, sizeof(voldir), "%s/ubi%u/ubi%u_%u",
/* todo: skip existing gluebi device for legacy support */
- volname = read_string_from_file(voldir, "name");
- if (!volname) {
+ if (!read_string_from_file(voldir, "name", volname, sizeof(volname))) {
ULOG_ERR("Couldn't read %s/name\n", voldir);
return NULL;
}