diff --git a/build_library/build_image_util.sh b/build_library/build_image_util.sh index 7927e89b04..33cd6c20d2 100755 --- a/build_library/build_image_util.sh +++ b/build_library/build_image_util.sh @@ -658,6 +658,7 @@ finish_image() { ${tmp_ignore} "${root_fs_dir}/var" sudo "${BUILD_LIBRARY_DIR}/gen_tmpfiles.py" --root="${root_fs_dir}" \ --output="${root_fs_dir}/usr/lib/tmpfiles.d/base_image_etc.conf" \ + --files="/usr/share/flatcar" \ ${tmp_ignore} "${root_fs_dir}/etc" # Only configure bootloaders if there is a boot partition @@ -692,6 +693,17 @@ EOF fi write_contents "${root_fs_dir}" "${BUILD_DIR}/${image_contents}" + # Backup the /etc contents to /usr/share/flatcar/etc to serve as source + # for creating missing files + sudo cp -a "${root_fs_dir}/etc" "${root_fs_dir}/usr/share/flatcar/etc" + # Remove the rootfs state as it should be recreated through the + # tmpfiles and may not be present on updating machines. This + # makes sure our tests cover the case of missing files in the + # rootfs and don't rely on the new image. Not done for the developer + # container. + if [[ -n "${image_kernel}" ]]; then + sudo rm --one-file-system -rf "${root_fs_dir}/etc" "${root_fs_dir}/var" + fi # Zero all fs free space to make it more compressible so auto-update # payloads become smaller, not fatal since it won't work on linux < 3.2 diff --git a/build_library/gen_tmpfiles.py b/build_library/gen_tmpfiles.py index a4b4134ba1..3d2b4b29c5 100755 --- a/build_library/gen_tmpfiles.py +++ b/build_library/gen_tmpfiles.py @@ -19,6 +19,7 @@ def main(): parser = optparse.OptionParser(description=__doc__) parser.add_option('--root', help='Remove root prefix from output') parser.add_option('--output', help='Write output to the given file') + parser.add_option('--files', default='', help='Also works on files and symlinks and uses the given path as source for copying files') parser.add_option('--ignore', action='append', default=[], help='Ignore one or more paths (use multiple times)') opts, args = parser.parse_args() @@ -32,8 +33,11 @@ def main(): assert path.startswith(opts.root) for dirpath, dirnames, filenames in os.walk(path): - if any(f.startswith('.keep') for f in filenames): - keep.add(dirpath) + keep.add(dirpath) + if opts.files: + for f in filenames: + if not f.startswith('.keep'): + keep.add(os.path.join(dirpath, f)) # Add all parent directories too for path in frozenset(keep): @@ -61,8 +65,22 @@ def main(): if stripped in opts.ignore: continue - info = os.stat(path) - assert stat.S_ISDIR(info.st_mode) + target = '-' + info = os.stat(path, follow_symlinks=False) + if not opts.files: + assert stat.S_ISDIR(info.st_mode) + if stat.S_ISDIR(info.st_mode): + entry = 'd' + elif stat.S_ISLNK(info.st_mode): + entry = 'L' + target = os.readlink(path) + elif stat.S_ISREG(info.st_mode): + entry = 'C' + assert opts.files + target = os.path.join(opts.files, stripped.lstrip('/')) + assert target.startswith(opts.files) + else: + continue mode = stat.S_IMODE(info.st_mode) try: @@ -74,8 +92,8 @@ def main(): except KeyError: group = str(info.st_gid) - config.append('d %-22s %04o %-10s %-10s - -' - % (stripped, mode, owner, group)) + config.append('%s %-22s %04o %-10s %-10s - %s' + % (entry, stripped, mode, owner, group, target)) if opts.output: fd = open(opts.output, 'w') diff --git a/changelog/changes/2022-03-30-create-etc-files.md b/changelog/changes/2022-03-30-create-etc-files.md new file mode 100644 index 0000000000..61fb085fa3 --- /dev/null +++ b/changelog/changes/2022-03-30-create-etc-files.md @@ -0,0 +1 @@ +- Added tmpfile rules to create missing folders, files, and symlinks under `/etc` and ship with an empty rootfs to ensure that we don't rely on files the updating machines may be missing and that all needed files have a tmpfile rule. To disable the creation of a file, symlink, or folder with the default contents, create an empty file or a symlink to `/dev/null` instead of removing it. ([PR#264](https://github.com/flatcar-linux/scripts/pull/264))