unify the basic fs layout using the initramfs txt
Rather than manually `mkdir` and `ln` every path we care about, use a
flat text file to describe things. We still create dirs we want to
install into, but the rest are now done by the generation tool.
BUG=brillo:845
TEST=`make` still builds all the targets
TEST=`USE='recovery_ramfs factory_shim_ramfs' emerge-link chromeos-initramfs` works
TEST=comparing contents files looks OK
Change-Id: I7edb24d288619c0b62423487b9b9b647d3439cee
Reviewed-on: https://chromium-review.googlesource.com/266273
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
Commit-Queue: Mike Frysinger <vapier@chromium.org>
Tested-by: Mike Frysinger <vapier@chromium.org>
diff --git a/common/process-layout.py b/common/process-layout.py
new file mode 100755
index 0000000..2cb119c
--- /dev/null
+++ b/common/process-layout.py
@@ -0,0 +1,127 @@
+#!/usr/bin/python2
+# Copyright 2015 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Helper tools related to the layout text file.
+
+First create a directory with the paths in it:
+$ %(progs)s make common/fs-layout.txt stagedir/
+
+Then create a reduced layout for later inclusion:
+$ %(progs)s filter common/fs-layout.txt new-layout.txt
+"""
+
+from __future__ import print_function
+
+import argparse
+import errno
+import os
+import sys
+
+
+def makedirs(path):
+ """Like os.makedirs, but ignore existing errors"""
+ try:
+ os.makedirs(path)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+
+def symlink(src, dst):
+ """Like os.symlink, but handle existing errors"""
+ try:
+ os.symlink(src, dst)
+ return
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+ # Assume the symlink has changed to make our lives simple.
+ os.unlink(dst)
+ os.symlink(src, dst)
+
+
+def ProcessLayout(layout):
+ """Yield each valid line in |layout| as a tuple of each element"""
+ # The number of elements expected for each object type.
+ valid_lens = {
+ 'file': (6, 7),
+ 'dir': (5,),
+ 'nod': (8,),
+ 'slink': (6,),
+ 'pipe': (5,),
+ 'sock': (5,),
+ }
+
+ with open(layout) as f:
+ for line in f:
+ line = line.split('#', 1)[0].strip()
+ if not line:
+ continue
+
+ elements = line.split()
+
+ etype = elements[0]
+ if etype not in valid_lens:
+ raise ValueError('Invalid line: unknown type "%s":\n%s' %
+ (etype, line))
+
+ valid_len = valid_lens[etype]
+ if len(elements) not in valid_len:
+ raise ValueError('Invalid line: wanted %r elements; got %i:\n%s' %
+ (valid_len, len(elements), line))
+
+ yield elements
+
+
+def GetParser():
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument('mode', choices=('make', 'filter'),
+ help='operation to perform')
+ parser.add_argument('layout', help='path to the filesystem layout file')
+ parser.add_argument('output', help='path to operate on')
+ return parser
+
+
+def main(argv):
+ parser = GetParser()
+ opts = parser.parse_args(argv)
+
+ if opts.mode == 'make':
+ # Create all the requested directories/files in the output directory.
+ # These paths are needed so we can install all files into the right
+ # layout w/out creating conflicts (e.g. /usr being a dir or a symlink).
+ for elements in ProcessLayout(opts.layout):
+ etype = elements.pop(0)
+ try:
+ if etype == 'dir':
+ path, mode, uid, gid = elements
+ assert ('0', '0') == (uid, gid)
+ mode = int(mode, 8)
+ path = os.path.join(opts.output, path.lstrip('/'))
+ makedirs(path)
+ os.chmod(path, mode)
+ elif etype == 'slink':
+ path, target, mode, uid, gid = elements
+ mode = int(mode, 8)
+ assert ('0', '0', 0o755) == (uid, gid, mode)
+ path = os.path.join(opts.output, path.lstrip('/'))
+ makedirs(os.path.dirname(path))
+ symlink(target, path)
+ except Exception:
+ print('While processing line: %s %s' % (etype, elements))
+ raise
+
+ elif opts.mode == 'filter':
+ # Filter out all the paths that 'make' above created. The stuff that is
+ # left often requires root access (which we don't have), but the cpio gen
+ # tool can take care of this for us.
+ with open(opts.output, 'a') as f:
+ for elements in ProcessLayout(opts.layout):
+ if elements[0] not in ('dir', 'slink'):
+ f.write(' '.join(elements) + '\n')
+
+
+if __name__ == '__main__':
+ main(sys.argv[1:])