├── README.md ├── LICENSE └── fs2json.py /README.md: -------------------------------------------------------------------------------- 1 | **This project is now maintained in the [v86](https://github.com/copy/v86) 2 | repository at 3 | [`tools/fs2json.py`](https://github.com/copy/v86/blob/master/tools/fs2json.py). The old readme is preserved below.** 4 | 5 | 6 | # fs2json 7 | 8 | ``` 9 | usage: fs2json.py [-h] [--exclude path] [--out [out]] path 10 | 11 | Create filesystem JSON. Example: 12 | ./fs2json.py --exclude /boot/ --out fs.json /mnt/ 13 | 14 | positional arguments: 15 | path Base path to include in JSON 16 | 17 | optional arguments: 18 | -h, --help show this help message and exit 19 | --exclude path Path to exclude (relative to base path). Can be specified multiple times. 20 | --out [out] File to write to (defaults to stdout) 21 | ``` 22 | 23 | 24 | This script will output something like (formatted for readability): 25 | 26 | ```json 27 | { 28 | "fsroot": [ 29 | ["bar", 4, 1421709361, 33188, 1000, 1000], 30 | ["bof", 12, 1421709395, 41471, 1000, 1000, "test/foo/bof"], 31 | ["foo", 4096, 1421709371, 16877, 1000, 1000, [ 32 | ["bof", 4, 1421709371, 33188, 1000, 1000], 33 | ["bar", 4096, 1421709348, 16877, 1000, 1000, []] 34 | ]] 35 | ], 36 | "size": 8212, 37 | "version": 2 38 | } 39 | ``` 40 | 41 | The current format is `[name, mode, mtime, size, uid, gid, target]` 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Fabian 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /fs2json.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import argparse 4 | import json 5 | import os 6 | import stat 7 | import sys 8 | import itertools 9 | import logging 10 | 11 | VERSION = 2 12 | 13 | IDX_NAME = 0 14 | IDX_SIZE = 1 15 | IDX_MTIME = 2 16 | IDX_MODE = 3 17 | IDX_UID = 4 18 | IDX_GID = 5 19 | 20 | # target for symbolic links 21 | # child nodes for directories 22 | # nothing for files 23 | IDX_TARGET = 6 24 | 25 | 26 | def main(): 27 | logging.basicConfig(format="%(message)s") 28 | logger = logging.getLogger("fs2json") 29 | logger.setLevel(logging.DEBUG) 30 | 31 | args = argparse.ArgumentParser(description="Create filesystem JSON. Example:\n" 32 | " ./fs2xml.py --exclude /boot/ --out fs.json /mnt/", 33 | formatter_class=argparse.RawTextHelpFormatter 34 | ) 35 | args.add_argument("--exclude", 36 | action="append", 37 | metavar="path", 38 | help="Path to exclude (relative to base path). Can be specified multiple times.") 39 | args.add_argument("--out", 40 | metavar="out", 41 | nargs="?", 42 | type=argparse.FileType("w"), 43 | help="File to write to (defaults to stdout)", 44 | default=sys.stdout) 45 | args.add_argument("path", 46 | metavar="path", 47 | help="Base path to include in JSON") 48 | 49 | args = args.parse_args() 50 | 51 | path = os.path.normpath(args.path) 52 | path = path + "/" 53 | exclude = args.exclude or [] 54 | exclude = [os.path.join("/", os.path.normpath(p)) for p in exclude] 55 | exclude = set(exclude) 56 | 57 | def onerror(oserror): 58 | logger.warning(oserror) 59 | 60 | rootdepth = path.count("/") 61 | files = os.walk(path, onerror=onerror) 62 | prevpath = [] 63 | 64 | mainroot = [] 65 | result = { 66 | "fsroot": mainroot, 67 | "version": VERSION, 68 | "size": 0, 69 | } 70 | rootstack = [mainroot] 71 | 72 | def make_node(st, name): 73 | obj = [None] * 7 74 | 75 | obj[IDX_NAME] = name 76 | obj[IDX_SIZE] = st.st_size 77 | obj[IDX_MTIME] = int(st.st_mtime) 78 | obj[IDX_MODE] = int(st.st_mode) 79 | 80 | obj[IDX_UID] = st.st_uid 81 | obj[IDX_GID] = st.st_gid 82 | 83 | result["size"] += st.st_size 84 | 85 | # Missing: 86 | # int(st.st_atime), 87 | # int(st.st_ctime), 88 | 89 | return obj 90 | 91 | logger.info("Creating file tree ...") 92 | 93 | for f in files: 94 | dirpath, dirnames, filenames = f 95 | pathparts = dirpath.split("/") 96 | pathparts = pathparts[rootdepth:] 97 | fullpath = os.path.join("/", *pathparts) 98 | 99 | if fullpath in exclude: 100 | dirnames[:] = [] 101 | continue 102 | 103 | depth = 0 104 | for this, prev in zip(pathparts, prevpath): 105 | if this != prev: 106 | break 107 | depth += 1 108 | 109 | for name in prevpath[depth:]: 110 | rootstack.pop() 111 | 112 | oldroot = rootstack[-1] 113 | 114 | assert len(pathparts[depth:]) == 1 115 | openname = pathparts[-1] 116 | 117 | if openname == "": 118 | root = mainroot 119 | else: 120 | root = [] 121 | st = os.stat(dirpath) 122 | rootobj = make_node(st, openname) 123 | rootobj[IDX_TARGET] = root 124 | oldroot.append(rootobj) 125 | 126 | rootstack.append(root) 127 | 128 | for filename in itertools.chain(filenames, dirnames): 129 | absname = os.path.join(dirpath, filename) 130 | 131 | st = os.lstat(absname) 132 | isdir = stat.S_ISDIR(st.st_mode) 133 | islink = stat.S_ISLNK(st.st_mode) 134 | 135 | if isdir and not islink: 136 | continue 137 | 138 | obj = make_node(st, filename) 139 | 140 | if islink: 141 | target = os.readlink(absname) 142 | obj[IDX_TARGET] = target 143 | 144 | while obj[-1] is None: 145 | obj.pop() 146 | 147 | root.append(obj) 148 | 149 | prevpath = pathparts 150 | 151 | logger.info("Creating json ...") 152 | 153 | json.dump(result, args.out, check_circular=False, separators=(',', ':')) 154 | 155 | if __name__ == "__main__": 156 | main() 157 | --------------------------------------------------------------------------------