/* Sets errno on error (!= 0), ENOSPC on short write */ int copy_file (const char *src_path, const char *dst_path, mode_t mode) { int sfd; int dfd; int res; int errsv; sfd = open (src_path, O_CLOEXEC | O_RDONLY); if (sfd == -1) return -1; dfd = creat (dst_path, mode); if (dfd == -1) { errsv = errno; close (sfd); errno = errsv; return -1; } res = copy_file_data (sfd, dfd); errsv = errno; close (sfd); close (dfd); errno = errsv; return res; }
static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { struct archive_entry *in_entry; int e; while (0 == archive_read_next_header(ina, &in_entry)) { if (!new_enough(bsdtar, archive_entry_pathname(in_entry), archive_entry_stat(in_entry))) continue; if (excluded(bsdtar, archive_entry_pathname(in_entry))) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); siginfo_setinfo(bsdtar, "copying", archive_entry_pathname(in_entry), archive_entry_size(in_entry)); siginfo_printinfo(bsdtar, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, 0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (copy_file_data(bsdtar, a, ina)) exit(1); } if (bsdtar->verbose) fprintf(stderr, "\n"); } /* Note: If we got here, we saw no write errors, so return success. */ return (0); }
static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { struct archive_entry *in_entry; int e; while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { if (!new_enough(bsdtar, archive_entry_pathname(in_entry), archive_entry_stat(in_entry))) continue; if (lafe_excluded(bsdtar->matching, archive_entry_pathname(in_entry))) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); if (need_report()) report_write(bsdtar, a, in_entry, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) lafe_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (copy_file_data(bsdtar, a, ina, in_entry)) exit(1); } if (bsdtar->verbose) fprintf(stderr, "\n"); } return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); }
int file_copy(const char *src, const char *dest) { struct stat src_stat; struct stat dest_stat; int dest_exists = 1; int status = 0; int r; r = stat(src, &src_stat); if (r < 0) { opkg_perror(ERROR, "%s", src); return -1; } r = stat(dest, &dest_stat); if (r < 0) { if (errno != ENOENT) { opkg_perror(ERROR, "unable to stat `%s'", dest); return -1; } dest_exists = 0; } else { int is_same_file = (src_stat.st_rdev == dest_stat.st_rdev && src_stat.st_ino == dest_stat.st_ino); if (is_same_file) { opkg_msg(ERROR, "`%s' and `%s' are the same file.\n", src, dest); return -1; } } if (S_ISREG(src_stat.st_mode)) { FILE *sfp, *dfp; struct utimbuf times; if (dest_exists) { dfp = fopen(dest, "w"); if (dfp == NULL) { r = unlink(dest); if (r < 0) { opkg_perror(ERROR, "unable to remove `%s'", dest); return -1; } } } else { int fd; fd = open(dest, O_WRONLY | O_CREAT, src_stat.st_mode); if (fd < 0) { opkg_perror(ERROR, "unable to open `%s'", dest); return -1; } dfp = fdopen(fd, "w"); if (dfp == NULL) { if (fd >= 0) close(fd); opkg_perror(ERROR, "unable to open `%s'", dest); return -1; } } sfp = fopen(src, "r"); if (sfp) { r = copy_file_data(sfp, dfp); if (r < 0) status = -1; r = fclose(sfp); if (r < 0) { opkg_perror(ERROR, "unable to close `%s'", src); status = -1; } } else { opkg_perror(ERROR, "unable to open `%s'", src); status = -1; } r = fclose(dfp); if (r < 0) { opkg_perror(ERROR, "unable to close `%s'", dest); status = -1; } times.actime = src_stat.st_atime; times.modtime = src_stat.st_mtime; r = utime(dest, ×); if (r < 0) opkg_perror(ERROR, "unable to preserve times of `%s'", dest); r = chown(dest, src_stat.st_uid, src_stat.st_gid); if (r < 0) { src_stat.st_mode &= ~(S_ISUID | S_ISGID); opkg_perror(ERROR, "unable to preserve ownership of `%s'", dest); } r = chmod(dest, src_stat.st_mode); if (r < 0) opkg_perror(ERROR, "unable to preserve permissions of `%s'", dest); return status; } else if (S_ISBLK(src_stat.st_mode) || S_ISCHR(src_stat.st_mode) || S_ISSOCK(src_stat.st_mode)) { r = mknod(dest, src_stat.st_mode, src_stat.st_rdev); if (r < 0) { opkg_perror(ERROR, "unable to create `%s'", dest); return -1; } } else if (S_ISFIFO(src_stat.st_mode)) { r = mkfifo(dest, src_stat.st_mode); if (r < 0) { opkg_perror(ERROR, "cannot create fifo `%s'", dest); return -1; } } else if (S_ISDIR(src_stat.st_mode)) { opkg_msg(ERROR, "%s: omitting directory.\n", src); return -1; } opkg_msg(ERROR, "internal error: unrecognized file type.\n"); return -1; }
static int handle_overwrite_open (const char *filename, const char *etag, gboolean create_backup, char **temp_filename, GCancellable *cancellable, GError **error) { int fd = -1; GLocalFileStat original_stat; char *current_etag; gboolean is_symlink; int open_flags; int res; /* We only need read access to the original file if we are creating a backup. * We also add O_CREATE to avoid a race if the file was just removed */ if (create_backup) open_flags = O_RDWR | O_CREAT | O_BINARY; else open_flags = O_WRONLY | O_CREAT | O_BINARY; /* Some systems have O_NOFOLLOW, which lets us avoid some races * when finding out if the file we opened was a symlink */ #ifdef O_NOFOLLOW is_symlink = FALSE; fd = g_open (filename, open_flags | O_NOFOLLOW, 0666); if (fd == -1 && errno == ELOOP) { /* Could be a symlink, or it could be a regular ELOOP error, * but then the next open will fail too. */ is_symlink = TRUE; fd = g_open (filename, open_flags, 0666); } #else fd = g_open (filename, open_flags, 0666); /* This is racy, but we do it as soon as possible to minimize the race */ is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK); #endif if (fd == -1) { int errsv = errno; char *display_name = g_filename_display_name (filename); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Error opening file '%s': %s"), display_name, g_strerror (errsv)); g_free (display_name); return -1; } #ifdef G_OS_WIN32 res = _fstati64 (fd, &original_stat); #else res = fstat (fd, &original_stat); #endif if (res != 0) { int errsv = errno; char *display_name = g_filename_display_name (filename); g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Error stating file '%s': %s"), display_name, g_strerror (errsv)); g_free (display_name); goto err_out; } /* not a regular file */ if (!S_ISREG (original_stat.st_mode)) { if (S_ISDIR (original_stat.st_mode)) g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY, _("Target file is a directory")); else g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_REGULAR_FILE, _("Target file is not a regular file")); goto err_out; } if (etag != NULL) { current_etag = _g_local_file_info_create_etag (&original_stat); if (strcmp (etag, current_etag) != 0) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_WRONG_ETAG, _("The file was externally modified")); g_free (current_etag); goto err_out; } g_free (current_etag); } /* We use two backup strategies. * The first one (which is faster) consist in saving to a * tmp file then rename the original file to the backup and the * tmp file to the original name. This is fast but doesn't work * when the file is a link (hard or symbolic) or when we can't * write to the current dir or can't set the permissions on the * new file. * The second strategy consist simply in copying the old file * to a backup file and rewrite the contents of the file. */ if (!(original_stat.st_nlink > 1) && !is_symlink) { char *dirname, *tmp_filename; int tmpfd; dirname = g_path_get_dirname (filename); tmp_filename = g_build_filename (dirname, ".goutputstream-XXXXXX", NULL); g_free (dirname); tmpfd = g_mkstemp (tmp_filename); if (tmpfd == -1) { g_free (tmp_filename); goto fallback_strategy; } /* try to keep permissions */ if ( #ifdef HAVE_FCHOWN fchown (tmpfd, original_stat.st_uid, original_stat.st_gid) == -1 || #endif #ifdef HAVE_FCHMOD fchmod (tmpfd, original_stat.st_mode) == -1 || #endif 0 ) { struct stat tmp_statbuf; /* Check that we really needed to change something */ if (fstat (tmpfd, &tmp_statbuf) != 0 || original_stat.st_uid != tmp_statbuf.st_uid || original_stat.st_gid != tmp_statbuf.st_gid || original_stat.st_mode != tmp_statbuf.st_mode) { close (tmpfd); g_unlink (tmp_filename); g_free (tmp_filename); goto fallback_strategy; } } close (fd); *temp_filename = tmp_filename; return tmpfd; } fallback_strategy: if (create_backup) { #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) struct stat tmp_statbuf; #endif char *backup_filename; int bfd; backup_filename = create_backup_filename (filename); if (g_unlink (backup_filename) == -1 && errno != ENOENT) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, _("Backup file creation failed")); g_free (backup_filename); goto err_out; } bfd = g_open (backup_filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, original_stat.st_mode & 0777); if (bfd == -1) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, _("Backup file creation failed")); g_free (backup_filename); goto err_out; } /* If needed, Try to set the group of the backup same as the * original file. If this fails, set the protection * bits for the group same as the protection bits for * others. */ #if defined(HAVE_FCHOWN) && defined(HAVE_FCHMOD) if (fstat (bfd, &tmp_statbuf) != 0) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, _("Backup file creation failed")); g_unlink (backup_filename); g_free (backup_filename); goto err_out; } if ((original_stat.st_gid != tmp_statbuf.st_gid) && fchown (bfd, (uid_t) -1, original_stat.st_gid) != 0) { if (fchmod (bfd, (original_stat.st_mode & 0707) | ((original_stat.st_mode & 07) << 3)) != 0) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, _("Backup file creation failed")); g_unlink (backup_filename); close (bfd); g_free (backup_filename); goto err_out; } } #endif if (!copy_file_data (fd, bfd, NULL)) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANT_CREATE_BACKUP, _("Backup file creation failed")); g_unlink (backup_filename); close (bfd); g_free (backup_filename); goto err_out; } close (bfd); g_free (backup_filename); /* Seek back to the start of the file after the backup copy */ if (lseek (fd, 0, SEEK_SET) == -1) { int errsv = errno; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Error seeking in file: %s"), g_strerror (errsv)); goto err_out; } } /* Truncate the file at the start */ #ifdef G_OS_WIN32 if (g_win32_ftruncate (fd, 0) == -1) #else if (ftruncate (fd, 0) == -1) #endif { int errsv = errno; g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errsv), _("Error truncating file: %s"), g_strerror (errsv)); goto err_out; } return fd; err_out: close (fd); return -1; }
static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina, void * cookie) { struct archive_entry *in_entry; int e; while (0 == archive_read_next_header(ina, &in_entry)) { if (truncate_archive(bsdtar)) break; if (checkpoint_archive(bsdtar, 0)) exit(1); if (cookie == NULL) disk_pause(bsdtar); if (network_select(0)) exit(1); if (!new_enough(bsdtar, archive_entry_pathname(in_entry), archive_entry_stat(in_entry))) continue; if (excluded(bsdtar, archive_entry_pathname(in_entry))) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); siginfo_setinfo(bsdtar, "copying", archive_entry_pathname(in_entry), archive_entry_size(in_entry)); siginfo_printinfo(bsdtar, 0); if (MODE_HEADER(bsdtar, a)) goto err_fatal; e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) bsdtar_warnc(bsdtar, 0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e < ARCHIVE_WARN) goto done; if (MODE_DATA(bsdtar, a)) goto err_fatal; if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (cookie == NULL) { if (copy_file_data(bsdtar, a, ina)) exit(1); } else { switch (archive_multitape_copy(ina, cookie, a, bsdtar->write_cookie)) { case -1: goto err_fatal; case -2: goto err_read; } } done: if (MODE_DONE(bsdtar, a)) goto err_fatal; if (bsdtar->verbose) fprintf(stderr, "\n"); continue; err_read: bsdtar->return_value = 1; if (MODE_DONE(bsdtar, a)) goto err_fatal; if (bsdtar->verbose) fprintf(stderr, "\n"); break; err_fatal: bsdtar_warnc(bsdtar, archive_errno(a), "%s", archive_error_string(a)); exit(1); } /* Note: If we got here, we saw no write errors, so return success. */ return (0); }
static void setup_newroot (bool unshare_pid, int privileged_op_socket) { SetupOp *op; for (op = ops; op != NULL; op = op->next) { cleanup_free char *source = NULL; cleanup_free char *dest = NULL; int source_mode = 0; int i; if (op->source && op->type != SETUP_MAKE_SYMLINK) { source = get_oldroot_path (op->source); source_mode = get_file_mode (source); if (source_mode < 0) die_with_error ("Can't get type of source %s", op->source); } if (op->dest) { dest = get_newroot_path (op->dest); if (mkdir_with_parents (dest, 0755, FALSE) != 0) die_with_error ("Can't mkdir parents for %s", op->dest); } switch (op->type) { case SETUP_RO_BIND_MOUNT: case SETUP_DEV_BIND_MOUNT: case SETUP_BIND_MOUNT: if (source_mode == S_IFDIR) { if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); } else { if (ensure_file (dest, 0666) != 0) die_with_error ("Can't create file at %s", op->dest); } privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, (op->type == SETUP_RO_BIND_MOUNT ? BIND_READONLY : 0) | (op->type == SETUP_DEV_BIND_MOUNT ? BIND_DEVICES : 0), source, dest); break; case SETUP_MOUNT_PROC: if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); if (unshare_pid) { /* Our own procfs */ privileged_op (privileged_op_socket, PRIV_SEP_OP_PROC_MOUNT, 0, dest, NULL); } else { /* Use system procfs, as we share pid namespace anyway */ privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, 0, "oldroot/proc", dest); } /* There are a bunch of weird old subdirs of /proc that could potentially be problematic (for instance /proc/sysrq-trigger lets you shut down the machine if you have write access). We should not have access to these as a non-privileged user, but lets cover them anyway just to make sure */ const char *cover_proc_dirs[] = { "sys", "sysrq-trigger", "irq", "bus" }; for (i = 0; i < N_ELEMENTS (cover_proc_dirs); i++) { cleanup_free char *subdir = strconcat3 (dest, "/", cover_proc_dirs[i]); privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, BIND_READONLY, subdir, subdir); } break; case SETUP_MOUNT_DEV: if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_TMPFS_MOUNT, 0, dest, NULL); static const char *const devnodes[] = { "null", "zero", "full", "random", "urandom", "tty" }; for (i = 0; i < N_ELEMENTS (devnodes); i++) { cleanup_free char *node_dest = strconcat3 (dest, "/", devnodes[i]); cleanup_free char *node_src = strconcat ("/oldroot/dev/", devnodes[i]); if (create_file (node_dest, 0666, NULL) != 0) die_with_error ("Can't create file %s/%s", op->dest, devnodes[i]); privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, node_src, node_dest); } static const char *const stdionodes[] = { "stdin", "stdout", "stderr" }; for (i = 0; i < N_ELEMENTS (stdionodes); i++) { cleanup_free char *target = xasprintf ("/proc/self/fd/%d", i); cleanup_free char *node_dest = strconcat3 (dest, "/", stdionodes[i]); if (symlink (target, node_dest) < 0) die_with_error ("Can't create symlink %s/%s", op->dest, stdionodes[i]); } { cleanup_free char *pts = strconcat (dest, "/pts"); cleanup_free char *ptmx = strconcat (dest, "/ptmx"); cleanup_free char *shm = strconcat (dest, "/shm"); if (mkdir (shm, 0755) == -1) die_with_error ("Can't create %s/shm", op->dest); if (mkdir (pts, 0755) == -1) die_with_error ("Can't create %s/devpts", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_DEVPTS_MOUNT, BIND_DEVICES, pts, NULL); if (symlink ("pts/ptmx", ptmx) != 0) die_with_error ("Can't make symlink at %s/ptmx", op->dest); } /* If stdout is a tty, that means the sandbox can write to the outside-sandbox tty. In that case we also create a /dev/console that points to this tty device. This should not cause any more access than we already have, and it makes ttyname() work in the sandbox. */ if (host_tty_dev != NULL && *host_tty_dev != 0) { cleanup_free char *src_tty_dev = strconcat ("/oldroot", host_tty_dev); cleanup_free char *dest_console = strconcat (dest, "/console"); if (create_file (dest_console, 0666, NULL) != 0) die_with_error ("creating %s/console", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, BIND_DEVICES, src_tty_dev, dest_console); } break; case SETUP_MOUNT_TMPFS: if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_TMPFS_MOUNT, 0, dest, NULL); break; case SETUP_MOUNT_MQUEUE: if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_MQUEUE_MOUNT, 0, dest, NULL); break; case SETUP_MAKE_DIR: if (mkdir (dest, 0755) != 0 && errno != EEXIST) die_with_error ("Can't mkdir %s", op->dest); break; case SETUP_MAKE_FILE: { cleanup_fd int dest_fd = -1; dest_fd = creat (dest, 0666); if (dest_fd == -1) die_with_error ("Can't create file %s", op->dest); if (copy_file_data (op->fd, dest_fd) != 0) die_with_error ("Can't write data to file %s", op->dest); close (op->fd); } break; case SETUP_MAKE_BIND_FILE: { cleanup_fd int dest_fd = -1; char tempfile[] = "/bindfileXXXXXX"; dest_fd = mkstemp (tempfile); if (dest_fd == -1) die_with_error ("Can't create tmpfile for %s", op->dest); if (copy_file_data (op->fd, dest_fd) != 0) die_with_error ("Can't write data to file %s", op->dest); close (op->fd); if (ensure_file (dest, 0666) != 0) die_with_error ("Can't create file at %s", op->dest); privileged_op (privileged_op_socket, PRIV_SEP_OP_BIND_MOUNT, 0, tempfile, dest); } break; case SETUP_MAKE_SYMLINK: if (symlink (op->source, dest) != 0) die_with_error ("Can't make symlink at %s", op->dest); break; default: die ("Unexpected type %d", op->type); } } privileged_op (privileged_op_socket, PRIV_SEP_OP_DONE, 0, NULL, NULL); }