From cb4ca7f73a32ef612591695a9fe773e14c8116c8 Mon Sep 17 00:00:00 2001 From: Serge Vakulenko Date: Fri, 1 Aug 2014 14:29:22 -0700 Subject: [PATCH] Fsutuil: move manifest routines to a separate file. --- tools/fsutil/Makefile | 6 +- tools/fsutil/fsutil.c | 150 +----------------- tools/fsutil/manifest.c | 318 ++++++++++++++++++++++++++++++++++++++ tools/fsutil/manifest.h | 81 ++++++++++ tools/fsutil/manifest.txt | 3 - 5 files changed, 410 insertions(+), 148 deletions(-) create mode 100644 tools/fsutil/manifest.c create mode 100644 tools/fsutil/manifest.h diff --git a/tools/fsutil/Makefile b/tools/fsutil/Makefile index cb39a8e..fa1a27b 100644 --- a/tools/fsutil/Makefile +++ b/tools/fsutil/Makefile @@ -1,7 +1,8 @@ CC = gcc -g CFLAGS = -O -Wall DESTDIR = /usr/local -OBJS = fsutil.o superblock.o block.c inode.o create.o check.o file.o mount.o +OBJS = fsutil.o superblock.o block.c inode.o create.o check.o \ + file.o mount.o manifest.o PROG = fsutil # For Mac OS X @@ -36,6 +37,7 @@ block.o: block.c bsdfs.h check.o: check.c bsdfs.h create.o: create.c bsdfs.h file.o: file.c bsdfs.h -fsutil.o: fsutil.c bsdfs.h +fsutil.o: fsutil.c bsdfs.h manifest.h inode.o: inode.c bsdfs.h +manifest.o: manifest.c bsdfs.h manifest.h superblock.o: superblock.c bsdfs.h diff --git a/tools/fsutil/fsutil.c b/tools/fsutil/fsutil.c index 135592f..a88058c 100644 --- a/tools/fsutil/fsutil.c +++ b/tools/fsutil/fsutil.c @@ -32,6 +32,7 @@ #include #include #include "bsdfs.h" +#include "manifest.h" int verbose; int extract; @@ -434,153 +435,12 @@ void add_contents (fs_t *fs, const char *dirname, const char *manifest) printf ("TODO: use manifest '%s'\n", manifest); } -/* - * Compare two entries of file traverse scan. - */ -static int ftsent_compare (const FTSENT **a, const FTSENT **b) -{ - return strcmp((*a)->fts_name, (*b)->fts_name); -} - -/* - * Store information about the link: dev, inode and path. - */ -typedef struct _link_info_t link_info_t; -struct _link_info_t { - link_info_t *next; - dev_t dev; - ino_t ino; - char path[1]; -}; - -static link_info_t *link_list; - -static void add_link (dev_t dev, ino_t ino, char *path) -{ - link_info_t *info; - - info = (link_info_t*) malloc (strlen (path) + sizeof (link_info_t)); - if (! info) { - fprintf (stderr, "%s: no memory for link info\n", path); - return; - } - info->dev = dev; - info->ino = ino; - strcpy (info->path, path); - - /* Insert into the list. */ - info->next = link_list; - link_list = info; -} - -/* - * Store information about the link: dev, inode and path. - */ -static char *find_link (dev_t dev, ino_t ino) -{ - link_info_t *info = link_list; - - for (info=link_list; info; info=info->next) { - if (info->dev == dev && info->ino == ino) - return info->path; - } - return 0; -} - -/* - * Create a manifest from directory contents. - */ -void manifest_scan (const char *dirname) -{ - FTS *dir; - FTSENT *node; - char *argv[2], *path, *target, buf[BSDFS_BSIZE]; - struct stat st; - int prefix_len, mode, len; - - argv[0] = (char*) dirname; - argv[1] = 0; - dir = fts_open (argv, FTS_PHYSICAL | FTS_NOCHDIR, &ftsent_compare); - if (! dir) { - fprintf (stderr, "%s: cannot open\n", dirname); - return; - } - prefix_len = strlen (dirname); - - printf ("# Manifest for directory %s\n", dirname); - for (;;) { - node = fts_read(dir); - if (! node) - break; - - path = node->fts_path + prefix_len; - if (path[0] == 0) - continue; - - st = *node->fts_statp; - mode = st.st_mode & 07777; - - switch (node->fts_info) { - case FTS_D: - /* Directory. */ - printf ("\ndir %s\n", path); - break; - - case FTS_F: - /* Regular file. */ - if (st.st_nlink > 1) { - /* Hard link to file. */ - target = find_link (st.st_dev, st.st_ino); - if (target) { - printf ("\nlink %s\n", path); - printf ("target %s\n", target); - continue; - } - add_link (st.st_dev, st.st_ino, path); - } - printf ("\nfile %s\n", path); - break; - - case FTS_SL: - /* Symlink. */ - if (st.st_nlink > 1) { - /* Hard link to symlink. */ - target = find_link (st.st_dev, st.st_ino); - if (target) { - printf ("\nlink %s\n", path); - printf ("target %s\n", target); - continue; - } - add_link (st.st_dev, st.st_ino, path); - } - printf ("\nsymlink %s\n", path); - - /* Get the target of symlink. */ - len = readlink(node->fts_accpath, buf, sizeof(buf) - 1); - if (len < 0) { - fprintf (stderr, "%s: cannot read\n", node->fts_accpath); - continue; - } - buf[len] = 0; - printf ("target %s\n", buf); - break; - - default: - /* Ignore all other variants. */ - continue; - } - printf ("mode %o\n", mode); - printf ("owner %u\n", st.st_uid); - printf ("group %u\n", st.st_gid); - } - fts_close (dir); -} - int main (int argc, char **argv) { int i, key; fs_t fs; fs_inode_t inode; + manifest_t m; const char *manifest = 0; for (;;) { @@ -673,7 +533,11 @@ int main (int argc, char **argv) if (scan) { /* Create a manifest from directory contents. */ - manifest_scan (argv[i]); + if (! manifest_scan (&m, argv[i])) { + fprintf (stderr, "%s: cannot read\n", argv[i]); + return -1; + } + manifest_print (&m); return 0; } diff --git a/tools/fsutil/manifest.c b/tools/fsutil/manifest.c new file mode 100644 index 0000000..5a6d517 --- /dev/null +++ b/tools/fsutil/manifest.c @@ -0,0 +1,318 @@ +/* + * Routines to handle manifest files. + * + * Copyright (C) 2014 Serge Vakulenko, + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ +#include +#include +#include +#include +#include +#include "bsdfs.h" +#include "manifest.h" + +/* + * Manifest entry. + */ +struct _entry_t { + entry_t *next; + int type; /* d, f, l, s, b or c */ + int mode; + int owner; + int group; + int major; + int minor; + char *link; /* Target for link or symlink */ + char path[1]; +}; + +/* + * Linked list of information for finding hard links. + */ +typedef struct _link_info_t link_info_t; +struct _link_info_t { + link_info_t *next; + dev_t dev; + ino_t ino; + char path[1]; +}; + +static link_info_t *link_list; /* List of hard links. */ + +/* + * Store information about the hard link: dev, inode and path. + */ +static void keep_link (dev_t dev, ino_t ino, char *path) +{ + link_info_t *info; + + info = (link_info_t*) malloc (strlen (path) + sizeof (link_info_t)); + if (! info) { + fprintf (stderr, "%s: no memory for link info\n", path); + return; + } + info->dev = dev; + info->ino = ino; + strcpy (info->path, path); + + /* Insert into the list. */ + info->next = link_list; + link_list = info; +} + +/* + * Find a hard link by dev and inode number. + * Return a path. + */ +static char *find_link (dev_t dev, ino_t ino) +{ + link_info_t *info = link_list; + + for (info=link_list; info; info=info->next) { + if (info->dev == dev && info->ino == ino) + return info->path; + } + return 0; +} + +/* + * Add new entry to the manifest. + */ +static void add_entry (manifest_t *m, int filetype, + char *path, char *link, int mode, int owner, int group) +{ + entry_t *e; + + e = malloc (sizeof(entry_t) + strlen (path)); + if (! e) { + fprintf (stderr, "%s: no memory for entry\n", path); + return; + } + + e->next = 0; + e->type = filetype; + e->mode = mode; + e->owner = owner; + e->group = group; + e->major = 0; + e->minor = 0; + e->link = 0; + e->link = link ? strdup (link) : 0; + strcpy (e->path, path); + + /* Append to the tail of the list. */ + if (m->first == 0) { + m->first = e; + } else { + m->last->next = e; + } + m->last = e; +} + +/* + * Compare two entries of file traverse scan. + */ +static int ftsent_compare (const FTSENT **a, const FTSENT **b) +{ + return strcmp((*a)->fts_name, (*b)->fts_name); +} + +/* + * Scan the directory and create a manifest from it's contents. + * Return 0 on error. + */ +int manifest_scan (manifest_t *m, const char *dirname) +{ + FTS *dir; + FTSENT *node; + char *argv[2], *path, *target, buf[BSDFS_BSIZE]; + struct stat st; + int prefix_len, mode, len; + + /* Clear manifest header. */ + m->first = 0; + m->last = 0; + m->filemode = 0; + m->dirmode = 0; + m->owner = 0; + m->group = 0; + + /* Open directory. */ + argv[0] = (char*) dirname; + argv[1] = 0; + dir = fts_open (argv, FTS_PHYSICAL | FTS_NOCHDIR, &ftsent_compare); + if (! dir) { + fprintf (stderr, "%s: cannot open\n", dirname); + return 0; + } + prefix_len = strlen (dirname); + + printf ("# Manifest for directory %s\n", dirname); + for (;;) { + /* Read next directory entry. */ + node = fts_read(dir); + if (! node) + break; + + path = node->fts_path + prefix_len; + if (path[0] == 0) + continue; + + st = *node->fts_statp; + mode = st.st_mode & 07777; + + switch (node->fts_info) { + case FTS_D: + /* Directory. */ + add_entry (m, 'd', path, 0, mode, st.st_uid, st.st_gid); + break; + + case FTS_F: + /* Regular file. */ + if (st.st_nlink > 1) { + /* Hard link to file. */ + target = find_link (st.st_dev, st.st_ino); + if (target) { + add_entry (m, 'l', path, target, mode, st.st_uid, st.st_gid); + break; + } + keep_link (st.st_dev, st.st_ino, path); + } + add_entry (m, 'f', path, 0, mode, st.st_uid, st.st_gid); + break; + + case FTS_SL: + /* Symlink. */ + if (st.st_nlink > 1) { + /* Hard link to symlink. */ + target = find_link (st.st_dev, st.st_ino); + if (target) { + add_entry (m, 'l', path, target, mode, st.st_uid, st.st_gid); + break; + } + keep_link (st.st_dev, st.st_ino, path); + } + /* Get the target of symlink. */ + len = readlink (node->fts_accpath, buf, sizeof(buf) - 1); + if (len < 0) { + fprintf (stderr, "%s: cannot read\n", node->fts_accpath); + break; + } + buf[len] = 0; + add_entry (m, 's', path, buf, mode, st.st_uid, st.st_gid); + break; + + default: + /* Ignore all other variants. */ + break; + } + } + fts_close (dir); + return 1; +} + +/* + * Dump the manifest to a text file. + */ +void manifest_print (manifest_t *m) +{ + void *cursor = 0; + char *path, *link; + int filetype, mode, owner, group, major, minor; + + cursor = 0; + while ((filetype = manifest_iterate (m, &cursor, &path, &link, &mode, + &owner, &group, &major, &minor)) != 0) + { + switch (filetype) { + case 'd': + /* Directory. */ + printf ("\ndir %s\n", path); + break; + case 'f': + /* Regular file. */ + printf ("\nfile %s\n", path); + break; + case 'l': + /* Hard link to file. */ + printf ("\nlink %s\n", path); + printf ("target %s\n", link); + continue; + case 's': + /* Symlink. */ + printf ("\nsymlink %s\n", path); + printf ("target %s\n", link); + break; + case 'b': + /* Block device. */ + printf ("\nbdev %s\n", path); + printf ("major %u\n", major); + printf ("minor %u\n", minor); + break; + case 'c': + /* Character device. */ + printf ("\ncdev %s\n", path); + printf ("major %u\n", major); + printf ("minor %u\n", minor); + break; + default: + /* Ignore all other variants. */ + continue; + } + printf ("mode %o\n", mode); + printf ("owner %u\n", owner); + printf ("group %u\n", group); + } +} + +/* + * Load a manifest from the text file. + * Return 0 on error. + */ +int manifest_load (manifest_t *m, const char *filename) +{ + //TODO + return 0; +} + +/* + * Iterate through the manifest. + */ +int manifest_iterate (manifest_t *m, void **last, char **path, char **link, + int *mode, int *owner, int *group, int *major, int *minor) +{ + /* Get the next entry. */ + entry_t *e = *last ? ((entry_t*)*last)->next : m->first; + + if (! e) + return 0; + + /* Fetch information about this entry. */ + *last = (void*) e; /* Pointer to a last processed entry. */ + *path = e->path; + *link = e->link; + *mode = (e->mode != -1) ? e->mode : (e->type == 'd') ? m->dirmode : m->filemode; + *owner = (e->owner != -1) ? e->owner : m->owner; + *group = (e->group != -1) ? e->group : m->group; + *major = e->major; + *minor = e->minor; + return e->type; +} diff --git a/tools/fsutil/manifest.h b/tools/fsutil/manifest.h new file mode 100644 index 0000000..063c0bd --- /dev/null +++ b/tools/fsutil/manifest.h @@ -0,0 +1,81 @@ +/* + * Routines to handle manifest files. + * + * Copyright (C) 2014 Serge Vakulenko, + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purpose and without fee is hereby + * granted, provided that the above copyright notice appear in all + * copies and that both that the copyright notice and this + * permission notice and warranty disclaimer appear in supporting + * documentation, and that the name of the author not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * + * The author disclaim all warranties with regard to this + * software, including all implied warranties of merchantability + * and fitness. In no event shall the author be liable for any + * special, indirect or consequential damages or any damages + * whatsoever resulting from loss of use, data or profits, whether + * in an action of contract, negligence or other tortious action, + * arising out of or in connection with the use or performance of + * this software. + */ + +/* + * Manifest header holds a linked list of file entries. + */ +typedef struct _manifest_t manifest_t; +typedef struct _entry_t entry_t; +struct _manifest_t { + entry_t *first; + entry_t *last; + int filemode; /* Default mode for files */ + int dirmode; /* Default mode for directories */ + int owner; /* Default owner */ + int group; /* Default group */ +}; + +/* + * Manifest entry. + */ + +/* + * Scan the directory and create a manifest from it's contents. + * Return 0 on error. + */ +int manifest_scan (manifest_t *m, const char *dirname); + +/* + * Load a manifest from the text file. + * Return 0 on error. + */ +int manifest_load (manifest_t *m, const char *filename); + +/* + * Dump the manifest to a text file. + */ +void manifest_print (manifest_t *m); + +/* + * Iterate through the manifest. + * Example: + * void *cursor = 0; + * char *path, *link; + * int filetype, mode, owner, group, major, minor; + * + * while ((filetype = manifest_iterate (m, &cursor, &path, &link, + * &mode, &owner, &group, &major, &minor)) != 0) + * { + * switch (filetype) { + * case 'd': // directory + * case 'f': // regular file + * case 'l': // hard link + * case 's': // symlink + * case 'b': // block device + * case 'c': // char device + * } + * } + */ +int manifest_iterate (manifest_t *m, void **cursor, char **path, char **link, + int *mode, int *owner, int *group, int *major, int *minor); diff --git a/tools/fsutil/manifest.txt b/tools/fsutil/manifest.txt index 6614e7d..5f29cac 100644 --- a/tools/fsutil/manifest.txt +++ b/tools/fsutil/manifest.txt @@ -6,9 +6,6 @@ owner 0 group 0 dirmode 775 filemode 664 -ctime now -mtime now -atime now dir /tmp