Fork me on GitHub

plash clean


Usage

plash clean

Description

Cleans up plashs internal data.
- Removes all broken links in $PLASH_DATA/index
- Removes all broken links in $PLASH_DATA/map
- Removes unused temporary directories in $PLASH_DATA/tmp

Tested Behaviour

#!/bin/sh
set -exu
# build container 2 and 3
plash build -f 1 --invalidate-layer

• A simple invocation succeeds

plash clean

• $PLASH_DATA/index is cleaned up

out_before=$(ls "$PLASH_DATA"/index)
plash rm 2
plash clean
out_after=$(ls "$PLASH_DATA"/index)
test "$out_before" != "$out_after"

• $PLASH_DATA/map is cleaned up

cont=$(plash build -f 1 --invalidate-layer)
plash map key1 "$cont"
plash map key2 "$cont"
plash map key3 "$cont"
out=$(ls "$PLASH_DATA"/map)
echo "$out" | grep key1
echo "$out" | grep key2
echo "$out" | grep key3
plash rm 3  # delete the container the maps are referencing to
plash clean --clean-cache-keys
out=$(ls "$PLASH_DATA"/map/)
test "$out" = ""

• $PLASH_DATA/tmp is cleaned up

out=$(ls "$PLASH_DATA"/tmp)
test "$out" = "" # assert empty tmp
plash b run -f 1 -- true
out=$(ls "$PLASH_DATA"/tmp)
echo "$out" | grep plashtmp # assert output contains plashtmp
plash clean --clean-tmp
out=$(ls "$PLASH_DATA"/tmp)
test "$out" = "" # assert empty tmp

• Unexpected files n $PLASH_DATA do not lead to crash

touch "$PLASH_DATA"/index/mybadfile
touch "$PLASH_DATA"/map/mybadfile
touch "$PLASH_DATA"/tmp/mybadfile
mkdir "$PLASH_DATA"/tmp/mybaddir
plash clean

Source Code


#define USAGE "usage: plash clean\n"

#include <dirent.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>

#include <plash.h>

size_t remove_broken_links_here() {
  size_t removed = 0;
  DIR *dir = opendir(".");
  if (dir == NULL)
    pl_fatal("opendir");
  struct dirent *entry;
  while ((entry = readdir(dir)) != NULL) {
    if (entry->d_type == DT_LNK) {
      if (access(entry->d_name, F_OK) == -1) {
        if (unlink(entry->d_name) != -1)
          removed++;
      }
    }
  }
  closedir(dir);
  return removed;
}

int is_process_still_running(pid_t pid, pid_t sid) {
  if (getsid(pid) != sid) {
    return EXIT_SUCCESS;
  }
  if (kill(pid, 0) == 0)
    return EXIT_FAILURE;
  return EXIT_SUCCESS;
}

size_t delete_unused_tmpdirs_here() {
  size_t count = 0;
  char *dirname_copy, *pid, *sid;

  DIR *dir = opendir(".");
  if (dir == NULL)
    pl_fatal("opendir");
  struct dirent *entry;
  while ((entry = readdir(dir)) != NULL) {
    if (entry->d_type == DT_DIR) {

      // parse sid and pid from the dir name
      if ((dirname_copy = strdup(entry->d_name)) == NULL)
        pl_fatal("strdup");
      strtok(dirname_copy, "_");
      if ((pid = strtok(NULL, "_")) == NULL)
        continue;
      if ((sid = strtok(NULL, "_")) == NULL)
        continue;

      // delete temporary directory if the process that created it already died.
      if (!is_process_still_running(atoll(pid), atoll(sid))) {
        pl_run("rm", "-rf", entry->d_name);
        count++;
      }

      free(dirname_copy);
    }
  }
  closedir(dir);
  return count;
}

int clean_main(int argc, char *argv[]) {
  size_t count;
  pl_unshare_user();
  char *pid, *sid;
  char *plash_data = pl_call("data");

  fprintf(stderr, "output_stable: false\n");

  // cd index
  if (chdir(plash_data) == -1)
    pl_fatal("chdir");
  if (chdir("index") == -1)
    pl_fatal("chdir");

  // remove broken indexes
  fprintf(stderr, "unlinked_indexes: ");
  count = remove_broken_links_here();
  fprintf(stderr, "%ld\n", count);

  // cd map
  if (chdir(plash_data) == -1)
    pl_fatal("chdir");
  if (chdir("map") == -1)
    pl_fatal("chdir");

  // remove broken maps
  fprintf(stderr, "unlinked_maps: ");
  count = remove_broken_links_here();
  fprintf(stderr, "%ld\n", count);

  // cd tmp
  if (chdir(plash_data) == -1)
    pl_fatal("chdir");
  if (chdir("tmp") == -1)
    pl_fatal("chdir");

  // remove unused tmp dirs
  fprintf(stderr, "removed_tmpdirs: ");
  count = delete_unused_tmpdirs_here();
  fprintf(stderr, "%ld\n", count);
  return EXIT_SUCCESS;
}