Files
swupd-client/src/helpers.c
T
Patrick McCarty b925a8481c Run clang-format on the code
The following command was run with clang 3.7.1:

$ clang-format -i -style=file include/*.h src/*.c test/*.c

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-03-07 14:23:18 -08:00

665 lines
12 KiB
C

/*
* Software Updater - client side
*
* Copyright © 2012-2016 Intel Corporation.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2 or later of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authors:
* Arjan van de Ven <arjan@linux.intel.com>
* Tim Pepper <timothy.c.pepper@linux.intel.com>
*
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mount.h>
#include <sys/statvfs.h>
#include <dirent.h>
#include <errno.h>
#include <stdarg.h>
#include "config.h"
#include "swupd.h"
void check_root(void)
{
if (getuid() != 0) {
printf("This program must be run as root..aborting.\n\n");
exit(EXIT_FAILURE);
}
}
/* Remove the contents of a staging directory (eg: /mnt/swupd/update/780 or
* /mnt/swupd/update/delta) which are not supposed to contain
* subdirectories containing files, ie: no need for true recursive removal.
* Just the relative path (et: "780" or "delta" is passed as a parameter).
*
* return: 0 on success, non-zero on error
*/
int rm_staging_dir_contents(const char *rel_path)
{
DIR *dir;
struct dirent entry;
struct dirent *result;
char *filename;
char *abs_path;
int ret;
string_or_die(&abs_path, "%s/%s", STATE_DIR, rel_path);
dir = opendir(abs_path);
if (dir == NULL) {
free(abs_path);
return -1;
}
while ((ret = readdir_r(dir, &entry, &result)) == 0) {
if (result == NULL) {
break;
}
if (!strcmp(entry.d_name, ".") ||
!strcmp(entry.d_name, "..")) {
continue;
}
string_or_die(&filename, "%s/%s", abs_path, entry.d_name);
ret = remove(filename);
if (ret != 0) {
free(filename);
break;
}
free(filename);
}
free(abs_path);
closedir(dir);
sync();
return ret;
}
void unlink_all_staged_content(struct file *file)
{
char *filename;
/* downloaded tar file */
string_or_die(&filename, "%s/download/%s.tar", STATE_DIR, file->hash);
unlink(filename);
free(filename);
string_or_die(&filename, "%s/download/.%s.tar", STATE_DIR, file->hash);
unlink(filename);
free(filename);
/* downloaded and un-tar'd file */
string_or_die(&filename, "%s/staged/%s", STATE_DIR, file->hash);
if (file->is_dir) {
rmdir(filename);
} else {
unlink(filename);
}
free(filename);
/* delta file */
if (file->peer) {
string_or_die(&filename, "%s/delta/%i-%i-%s", STATE_DIR,
file->peer->last_change, file->last_change, file->hash);
unlink(filename);
free(filename);
}
}
FILE *fopen_exclusive(const char *filename) /* no mode, opens for write only */
{
int fd;
fd = open(filename, O_CREAT | O_EXCL | O_RDWR, 00600);
if (fd < 0) {
return NULL;
}
return fdopen(fd, "w");
}
int create_required_dirs(void)
{
int ret = 0;
int i;
char *dir;
#define STATE_DIR_COUNT 3
const char *dirs[] = { "delta", "staged", "download" };
struct stat buf;
bool missing = false;
// check for existance
ret = stat(STATE_DIR, &buf);
if (ret && (errno == ENOENT)) {
missing = true;
}
for (i = 0; i < STATE_DIR_COUNT; i++) {
string_or_die(&dir, "%s/%s", STATE_DIR, dirs[i]);
ret = stat(dir, &buf);
if (ret) {
missing = true;
}
free(dir);
}
if (missing) { // (re)create dirs
char *cmd;
// laziness here for want of a simple "mkdir -p"
string_or_die(&cmd, "mkdir -p %s/{delta,staged,download}", STATE_DIR);
ret = system(cmd);
if (ret) {
return -1;
}
free(cmd);
// chmod 700
ret = chmod(STATE_DIR, S_IRWXU);
if (ret) {
return -1;
}
for (i = 0; i < STATE_DIR_COUNT; i++) {
string_or_die(&dir, "%s/%s", STATE_DIR, dirs[i]);
ret = chmod(dir, S_IRWXU);
if (ret) {
return -1;
}
free(dir);
}
}
return 0;
}
/**
* store a colon separated list of current mountpoint into
* variable mounted_dirs, this function do not return a value.
*
* e.g: :/proc:/mnt/acct:
*/
void get_mounted_directories(void)
{
FILE *file;
char *line = NULL;
char *mnt;
char *tmp;
ssize_t ret;
char *c;
size_t n;
file = fopen("/proc/self/mountinfo", "r");
if (!file) {
return;
}
while (!feof(file)) {
ret = getline(&line, &n, file);
if ((ret < 0) || (line == NULL)) {
break;
}
c = strchr(line, '\n');
if (c) {
*c = 0;
}
n = 0;
mnt = strtok(line, " ");
while (mnt != NULL) {
if (n == 4) {
/* The "4" assumes today's mountinfo form of:
* 16 36 0:3 / /proc rw,relatime master:7 - proc proc rw
* where the fifth field is the mountpoint. */
if (strcmp(mnt, "/") == 0) {
break;
}
if (mounted_dirs == NULL) {
string_or_die(&mounted_dirs, "%s", ":");
}
tmp = mounted_dirs;
string_or_die(&mounted_dirs, "%s%s:", tmp, mnt);
free(tmp);
break;
}
n++;
mnt = strtok(NULL, " ");
}
free(line);
line = NULL;
}
free(line);
fclose(file);
}
// prepends prefix to an path (eg: the global path_prefix to a
// file->filename or some other path prefix and path), insuring there
// is no duplicate '/' at the strings' junction and no trailing '/'
char *mk_full_filename(const char *prefix, const char *path)
{
char *fname = NULL;
char *abspath;
if (path[0] == '/') {
abspath = strdup(path);
} else {
string_or_die(&abspath, "/%s", path);
}
if (abspath == NULL) {
abort();
}
// The prefix is a minimum of "/" or "". If the prefix is only that,
// just use abspath. If the prefix is longer than the minimal, insure
// it ends in not "/" and append abspath.
if ((strcmp(prefix, "/") == 0) ||
(strcmp(prefix, "") == 0)) {
// rootfs, use absolute path
fname = strdup(abspath);
if (fname == NULL) {
abort();
}
} else if (strcmp(&prefix[strlen(prefix) - 1], "/") == 0) {
// chroot and need to strip trailing "/" from prefix
char *tmp = strdup(prefix);
if (tmp == NULL) {
abort();
}
tmp[strlen(tmp) - 1] = '\0';
string_or_die(&fname, "%s%s", tmp, abspath);
free(tmp);
} else {
// chroot and no need to strip trailing "/" from prefix
string_or_die(&fname, "%s%s", prefix, abspath);
}
free(abspath);
return fname;
}
// expects filename w/o path_prefix prepended
bool is_directory_mounted(const char *filename)
{
char *fname;
bool ret = false;
char *tmp;
if (mounted_dirs == NULL) {
return false;
}
tmp = mk_full_filename(path_prefix, filename);
string_or_die(&fname, ":%s:", tmp);
free(tmp);
if (strstr(mounted_dirs, fname)) {
ret = true;
}
free(fname);
return ret;
}
// expects filename w/o path_prefix prepended
bool is_under_mounted_directory(const char *filename)
{
bool ret = false;
int err;
char *token;
char *mountpoint;
char *dir;
char *fname;
char *tmp;
if (mounted_dirs == NULL) {
return false;
}
dir = strdup(mounted_dirs);
if (dir == NULL) {
abort();
}
token = strtok(dir + 1, ":");
while (token != NULL) {
string_or_die(&mountpoint, "%s/", token);
tmp = mk_full_filename(path_prefix, filename);
string_or_die(&fname, ":%s:", tmp);
free(tmp);
err = strncmp(fname, mountpoint, strlen(mountpoint));
free(fname);
if (err == 0) {
free(mountpoint);
ret = true;
break;
}
token = strtok(NULL, ":");
free(mountpoint);
}
free(dir);
return ret;
}
static int swupd_rm_file(const char *path)
{
int err = unlink(path);
if (err) {
if (errno == ENOENT) {
return 0;
} else {
return -1;
}
}
return 0;
}
static int swupd_rm_dir(const char *path)
{
DIR *dir;
struct dirent entry;
struct dirent *result;
char *filename = NULL;
int ret, err;
dir = opendir(path);
if (dir == NULL) {
if (errno == ENOENT) {
ret = 0;
goto exit;
} else {
ret = -1;
goto exit;
}
}
while ((ret = readdir_r(dir, &entry, &result)) == 0) {
if (result == NULL) {
break;
}
if (!strcmp(entry.d_name, ".") ||
!strcmp(entry.d_name, "..")) {
continue;
}
free(filename);
string_or_die(&filename, "%s/%s", path, entry.d_name);
err = swupd_rm(filename);
if (err) {
ret = -1;
goto exit;
}
}
/* Delete directory once it's empty */
err = rmdir(path);
if (err) {
if (errno == ENOENT) {
} else {
ret = -1;
goto exit;
}
}
exit:
closedir(dir);
free(filename);
return ret;
}
int swupd_rm(const char *filename)
{
struct stat stat;
int ret;
ret = lstat(filename, &stat);
if (ret) {
if (errno == ENOENT) {
// Quiet, no real failure here
return -ENOENT;
} else {
return -1;
}
}
if (S_ISDIR(stat.st_mode)) {
ret = swupd_rm_dir(filename);
} else {
ret = swupd_rm_file(filename);
}
return ret;
}
int rm_bundle_file(const char *bundle)
{
char *filename = NULL;
int ret = 0;
struct stat statb;
string_or_die(&filename, "%s/%s/%s", path_prefix, BUNDLES_DIR, bundle);
if (stat(filename, &statb) == -1) {
goto out;
}
if (S_ISREG(statb.st_mode)) {
if (unlink(filename) != 0) {
ret = -1;
goto out;
}
} else {
ret = -1;
goto out;
}
out:
free(filename);
return ret;
}
#if 0
void dump_file_info(struct file *file)
{
printf("%s:\n", file->filename);
printf("\t%s\n", file->hash);
printf("\t%d\n", file->last_change);
if (file->use_xattrs) {
printf("\tuse_xattrs\n");
}
if (file->is_dir) {
printf("\tis_dir\n");
}
if (file->is_file) {
printf("\tis_file\n");
}
if (file->is_link) {
printf("\tis_link\n");
}
if (file->is_deleted) {
printf("\tis_deleted\n");
}
if (file->is_manifest) {
printf("\tis_manifest\n");
}
if (file->is_config) {
printf("\tis_config\n");
}
if (file->is_state) {
printf("\tis_state\n");
}
if (file->is_boot) {
printf("\tis_boot\n");
}
if (file->is_rename) {
printf("\tis_rename\n");
}
if (file->is_orphan) {
printf("\tis_orphan\n");
}
if (file->do_not_update) {
printf("\tdo_not_update\n");
}
if (file->peer) {
printf("\tpeer %s(%s)\n", file->peer->filename, file->peer->hash);
}
if (file->deltapeer) {
printf("\tdeltapeer %s(%s)\n", file->deltapeer->filename, file->deltapeer->hash);
}
if (file->staging) {
printf("\tstaging %s\n", file->staging);
}
}
#endif
void free_file_data(void *data)
{
struct file *file = (struct file *)data;
if (!file) {
return;
}
/* peer and deltapeer are pointers to files contained
* in another list and must not be disposed */
if (file->filename) {
free(file->filename);
}
if (file->header) {
free(file->header);
}
if (file->staging) {
free(file->staging);
}
free(file);
}
/* this function is intended to encapsulate the basic swupd
* initializations for the majority of commands, that is:
* - Make sure root is the user running the code
* - Initialize log facility
* - Get the lock
* - initialize mounted directories
* - Initialize curl
*/
int swupd_init(int *lock_fd)
{
int ret = 0;
check_root();
*lock_fd = p_lockfile();
if (*lock_fd < 0) {
ret = ELOCK_FILE;
goto out_fds;
}
get_mounted_directories();
if (swupd_curl_init() != 0) {
ret = ECURL_INIT;
goto out_close_lock;
}
return ret;
out_close_lock:
v_lockfile(*lock_fd);
out_fds:
dump_file_descriptor_leaks();
return ret;
}
/* this function prints the initial message for all utils
*/
void copyright_header(const char *name)
{
printf(PACKAGE " %s " VERSION "\n", name);
printf(" Copyright (C) 2012-2016 Intel Corporation\n");
printf("\n");
}
void string_or_die(char **strp, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (vasprintf(strp, fmt, ap) <= 0) {
abort();
}
va_end(ap);
}
void update_motd(int new_release)
{
FILE *motd_fp = NULL;
motd_fp = fopen(MOTD_FILE, "w");
if (motd_fp != NULL) {
fprintf(motd_fp, "There is a new OS version available: %d\n", new_release);
fprintf(motd_fp, "Upgrade to the latest version using 'swupd update'\n");
fclose(motd_fp);
}
}
void delete_motd(void)
{
if (unlink(MOTD_FILE) == ENOMEM) {
abort();
}
}
int is_dirname_link(const char *fullname)
{
int ret = -1;
char *real_path = NULL;
real_path = realpath(fullname, NULL);
if (!real_path) {
printf("Failed to get real path of %s\n", fullname);
return -1;
}
if (strcmp(real_path, fullname) != 0) {
ret = 1;
} else {
ret = 0;
}
free(real_path);
return ret;
}