Exemplo n.º 1
0
static int
merge_dirs (const char *root, char **dirs, int n_dirs)
{
  DIR *dir;
  char *subdirs[n_dirs];
  struct dirent *dirent;
  struct stat st;
  char *src_path;
  char *dest_path;
  int conflict;
  int i, j;

  for (i = 0; i < n_dirs; i++)
    {
      if (dirs[i] == NULL)
        continue;

      dir = opendir (dirs[i]);
      if (dir == NULL)
        continue;

      while ((dirent = readdir (dir)) != NULL)
        {
          src_path = strconcat (dirs[i], "/", dirent->d_name);

          if (strcmp (dirent->d_name, ".") == 0 ||
              strcmp (dirent->d_name, "..") == 0)
            continue;

          dest_path = strconcat (root, "/", dirent->d_name);

          if (lstat (dest_path, &st) == 0)
            {
              free (dest_path);
              continue; /* We already copyed this file */
            }

          if (lstat (src_path, &st) < 0)
            {
              free (dest_path);
              continue;
            }

          if (S_ISCHR (st.st_mode) ||
              S_ISBLK (st.st_mode) ||
              S_ISFIFO (st.st_mode) ||
              S_ISSOCK (st.st_mode))
            {
              fprintf (stderr, "WARNING: ignoring special file %s\n", src_path);
              free (dest_path);
              continue;
            }

          conflict = has_conflict (dirs, n_dirs, dirent->d_name, i);

          if (conflict == NO_CONFLICTS)
            {
              bind_file (src_path, dest_path, &st);
            }
          else if (conflict == DIR_CONFLICT)
            {
              if (mkdir (dest_path, st.st_mode & 0777))
                fatal_errno ("create merged dir");

              if (lchown(dest_path, st.st_uid, st.st_gid) < 0)
                fatal_errno ("lchown");

              for (j = 0; j < n_dirs; j++)
                subdirs[j] = get_subdir (dirs[j], dirent->d_name);

              merge_dirs (dest_path, subdirs, n_dirs);
              for (j = 0; j < n_dirs; j++)
                {
                  if (subdirs[j])
                    free (subdirs[j]);
                }
            }
          else
            fatal ("Filename conflicts, refusing to mount\n");

          free (dest_path);
        }
    }

  return 0;
}
Exemplo n.º 2
0
static void
perform_merge(int op)
{
#ifndef _WIN32
	struct stat src, dst;
#endif

	ops_t *ops;

	create_empty_dir("first");
	create_empty_dir("first/nested1");
	create_empty_dir("first/nested1/nested2");
	create_empty_file("first/nested1/nested2/file");

	create_empty_dir("second");
	create_empty_dir("second/nested1");

#ifndef _WIN32
	/* Something about GNU Hurd and OS X differs, so skip this workaround there.
	 * Really need to figure out what's wrong with this thing... */
#if !defined(__gnu_hurd__) && !defined(__APPLE__)
	{
		struct timeval tv[2];
		gettimeofday(&tv[0], NULL);
		tv[1] = tv[0];

		/* This might be Linux-specific, but for the test to work properly access
		 * time should be newer than modification time, in which case it's not
		 * changed on listing directory. */
		tv[0].tv_sec += 3;
		tv[0].tv_usec += 4;
		tv[1].tv_sec += 1;
		tv[1].tv_usec += 2;
		utimes("first/nested1", tv);
	}
#endif
	assert_success(chmod("first/nested1", 0700));
	assert_success(os_stat("first/nested1", &src));
#endif

	cmd_group_begin("undo msg");

	assert_non_null(ops = ops_alloc(op, 0, "merge", ".", "."));
	ops->crp = CRP_OVERWRITE_ALL;
	if(op == OP_MOVEF)
	{
		assert_success(merge_dirs("first", "second", ops));
	}
	else
	{
#ifndef _WIN32
		if(!cfg.use_system_calls)
		{
			assert_success(
					perform_operation(op, ops, NULL, "first/nested1", "second/"));
		}
		else
#endif
		{
			assert_success(perform_operation(op, ops, NULL, "first", "second"));
		}
	}
	ops_free(ops);

	cmd_group_end();

#ifndef _WIN32
	{
		assert_success(os_stat("second/nested1", &dst));
#ifndef HAVE_STRUCT_STAT_ST_MTIM
#define st_atim st_atime
#define st_mtim st_mtime
#endif
		assert_success(memcmp(&src.st_atim, &dst.st_atim, sizeof(src.st_atim)));
		assert_success(memcmp(&src.st_mtim, &dst.st_mtim, sizeof(src.st_mtim)));
		assert_success(memcmp(&src.st_mode, &dst.st_mode, sizeof(src.st_mode)));
	}
#endif

	assert_true(file_exists("second/nested1/nested2/file"));

	assert_success(unlink("second/nested1/nested2/file"));
	assert_success(rmdir("second/nested1/nested2"));
	assert_success(rmdir("second/nested1"));
	assert_success(rmdir("second"));
}
Exemplo n.º 3
-1
int
main (int argc,
      char **argv)
{
  char tempdir[] = "/tmp/approot_XXXXXX";
  char *base_os;
  char **images;
  char *root;
  int n_images;
  pid_t child;
  int child_status = 0;
  char *app_root;
  char **mountpoints;
  int n_mountpoints;
  int i;
  uid_t ruid, euid, suid;
  gid_t rgid, egid, sgid;
  char cwd_buf[PATH_MAX];
  char *cwd;

  if (argc < 2)
    fatal ("Too few arguments, need base and at least one image");

  base_os = argv[1];
  images = &argv[2];
  n_images = argc - 2;

  root = mkdtemp (tempdir);
  if (root == NULL)
    fatal ("Can't create root");

  if (getresgid (&rgid, &egid, &sgid) < 0)
    fatal_errno ("getresgid");
  if (getresuid (&ruid, &euid, &suid) < 0)
    fatal_errno ("getresuid");

  if ((child = syscall (__NR_clone, SIGCHLD | CLONE_NEWNS, NULL)) < 0)
    fatal_errno ("clone");

  if (child == 0)
    {
      /* Child */

      /* Disable setuid, new caps etc for children */
      if (prctl (PR_SET_NO_NEW_PRIVS, 1) < 0 && errno != EINVAL)
        fatal_errno ("prctl (PR_SET_NO_NEW_PRIVS)");
      else if (prctl (PR_SET_SECUREBITS,
                      SECBIT_NOROOT | SECBIT_NOROOT_LOCKED) < 0)
        fatal_errno ("prctl (SECBIT_NOROOT)");

      /* Don't leak our mounts to the parent namespace */
      if (mount (NULL, "/", "none", MS_SLAVE | MS_REC, NULL) < 0)
        fatal_errno ("mount(/, MS_SLAVE | MS_REC)");

      /* Check we're allowed to chdir into base os */
      cwd = getcwd (cwd_buf, sizeof (cwd_buf));
      if (fsuid_chdir (ruid, base_os) < 0)
        fatal_errno ("chdir");
      if (chdir (cwd) < 0)
        fatal_errno ("chdir");

      if (mount ("tmpfs", root, "tmpfs",
                 MS_MGC_VAL | MS_PRIVATE, NULL) != 0)
        fatal_errno ("execv");

      n_mountpoints = n_images + 1;
      mountpoints = calloc (n_mountpoints, sizeof (char *));
      if (mountpoints == NULL)
        fatal ("oom");

      mountpoints[0] = base_os;

      for (i = 0; i < n_images; i++)
        {
          if (fsuid_access (ruid, images[i], R_OK) < 0)
            fatal_errno ("access");

          mountpoints[i+1] = mount_image (root, images[i]);
          if (mountpoints[i+1] == NULL)
            fatal ("mount image %s\n", images[i]);
        }

      app_root = make_fs_dir (root, "/root", 0555);
      if (app_root == NULL)
        fatal ("make_fs_dir root");

      setup_base (app_root);

      merge_dirs (app_root, mountpoints, n_mountpoints);

      if (chdir (app_root) < 0)
        fatal_errno ("chdir");

      if (chroot (".") < 0)
        fatal_errno ("chroot");

      /* Switch back to the uid of our invoking process.  These calls are
       * irrevocable - see setuid(2) */
      if (setgid (rgid) < 0)
        fatal_errno ("setgid");
      if (setuid (ruid) < 0)
        fatal_errno ("setuid");

      if (execl ("/bin/sh", "/bin/sh", NULL) < 0)
        fatal_errno ("execl");
    }

  /* Parent */

  /* Let's also setuid back in the parent - there's no reason to stay uid 0, and
   * it's just better to drop privileges. */
  if (setgid (rgid) < 0)
    fatal_errno ("setgid");
  if (setuid (ruid) < 0)
    fatal_errno ("setuid");

  if (child == -1)
    fatal_errno ("clone");

  /* Ignore Ctrl-C in parent while waiting */
  signal (SIGINT, SIG_IGN);

  if (waitpid (child, &child_status, 0) < 0)
    fatal_errno ("waitpid");

  rmdir (root);

  if (WIFEXITED (child_status))
    return WEXITSTATUS (child_status);
  else
    return 1;
}