/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1991-1998 University of Maryland at College Park
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
/*
* $Id: holding.c,v 1.56 2006/06/09 23:07:26 martinea Exp $
*
* Functions to access holding disk
*/
#include "amanda.h"
#include "amutil.h"
#include "holding.h"
#include "diskfile.h"
#include "fileheader.h"
#include "logfile.h"
/*
* utilities */
/* Is fname a directory?
*
* @param fname: filename (fully qualified)
* @returns: boolean
*/
static int is_dir(char *fname);
/* Is fname an empty file?
*
* @param fname: filename (fully qualified)
* @returns: boolean
*/
static int is_emptyfile(char *fname);
/* sanity check that datestamp is of the form YYYYMMDD or
* YYYYMMDDhhmmss
*
* @param fname: a filename (without directory)
* @returns: boolean
*/
static int is_datestr(char *fname);
gboolean take_holding_pid(char *diskdir, int pid);
static int can_take_holding(char *pid_file, int remove);
/*
* Static functions */
static int
is_dir(
char *fname)
{
struct stat statbuf;
if(stat(fname, &statbuf) == -1) return 0;
return (statbuf.st_mode & S_IFDIR) == S_IFDIR;
}
static int
is_emptyfile(
char *fname)
{
struct stat statbuf;
if(stat(fname, &statbuf) == -1) return 0;
return ((statbuf.st_mode & S_IFDIR) != S_IFDIR) &&
(statbuf.st_size == (off_t)0);
}
static int
is_datestr(
char *fname)
{
char *cp;
int ch, num, date, year, month, hour, minute, second;
char ymd[9], hms[7];
/* must be 8 digits */
for(cp = fname; (ch = *cp) != '\0'; cp++) {
if(!isdigit(ch)) {
break;
}
}
if(ch != '\0' || (cp-fname != 8 && cp-fname != 14)) {
return 0;
}
/* sanity check year, month, and day */
strncpy(ymd, fname, 8);
ymd[8] = '\0';
num = atoi(ymd);
year = num / 10000;
month = (num / 100) % 100;
date = num % 100;
if(year<1990 || year>2100 || month<1 || month>12 || date<1 || date>31)
return 0;
if(cp-fname == 8)
return 1;
/* sanity check hour, minute, and second */
strncpy(hms, fname+8, 6);
hms[6] = '\0';
num = atoi(hms);
hour = num / 10000;
minute = (num / 100) % 100;
second = num % 100;
if(hour> 23 || minute>59 || second>59)
return 0;
/* yes, we passed all the checks */
return 1;
}
/*
* Recursion functions
*
* These implement a general-purpose walk down the holding-* hierarchy.
*/
/* Perform a custom action for this holding element (disk, dir, file, chunk).
*
* If the element is not cruft, the next step into the tree will only take place
* if this function returns a nonzero value.
*
* The walk is depth-first, with the callback for an element invoked
* before entering that element. Callbacks may depend on this behavior.
*
* @param datap: generic user-data pointer
* @param base: the parent of the element being examined, or NULL for
* holding disks
* @param element: the name of the element being examined
* @param fqpath: fully qualified path to 'element'
* @param is_cruft: nonzero if this element doesn't belong here
* @returns: nonzero if the walk should descend into this element.
*/
typedef int (*holding_walk_fn)(
gpointer datap,
char *base,
char *element,
char *fqpath,
int is_cruft);
typedef enum {
STOP_AT_DISK,
STOP_AT_DIR,
STOP_AT_FILE,
STOP_AT_CHUNK
} stop_at_t;
/* Recurse over all holding chunks in a holding file.
*
* Call per_chunk_fn for each chunk of the given file
*
* datap is passed, unchanged, to all holding_walk_fns.
*
* @param hfile: holding file to examine (fully qualified path)
* @param datap: generic user-data pointer
* @param per_chunk_fn: function to call for each holding chunk
*/
static void holding_walk_file(
char *hfile,
gpointer datap,
holding_walk_fn per_chunk_fn)
{
dumpfile_t file;
char *filename = NULL;
/* Loop through all cont_filenames (subsequent chunks) */
filename = g_strdup(hfile);
while (filename != NULL && filename[0] != '\0') {
int is_cruft = 0;
/* get the header to look for cont_filename */
if (!holding_file_get_dumpfile(filename, &file)) {
is_cruft = 1;
}
if (per_chunk_fn)
per_chunk_fn(datap,
hfile,
filename,
filename,
is_cruft);
amfree(filename);
/* and go on to the next chunk if this wasn't cruft */
if (!is_cruft)
filename = g_strdup(file.cont_filename);
dumpfile_free_data(&file);
}
amfree(filename);
}
/* Recurse over all holding files in a holding directory.
*
* Call per_file_fn for each file, and so on, stopping at the level given by
* stop_at.
*
* datap is passed, unchanged, to all holding_walk_fns.
*
* @param hdir: holding directory to examine (fully qualified path)
* @param datap: generic user-data pointer
* @param stop_at: do not proceed beyond this level of the hierarchy
* @param per_file_fn: function to call for each holding file
* @param per_chunk_fn: function to call for each holding chunk
*/
static void holding_walk_dir(
char *hdir,
gpointer datap,
stop_at_t stop_at,
holding_walk_fn per_file_fn,
holding_walk_fn per_chunk_fn)
{
DIR *dir;
struct dirent *workdir;
char *hfile = NULL;
dumpfile_t dumpf;
int dumpf_ok;
int proceed = 1;
if ((dir = opendir(hdir)) == NULL) {
if (errno != ENOENT)
dbprintf(_("Warning: could not open holding dir %s: %s\n"),
hdir, strerror(errno));
return;
}
while ((workdir = readdir(dir)) != NULL) {
int is_cruft = 0;
if (is_dot_or_dotdot(workdir->d_name))
continue; /* expected cruft */
g_free(hfile);
hfile = g_strconcat(hdir, "/", workdir->d_name, NULL);
/* filter out various undesirables */
if (is_emptyfile(hfile))
is_cruft = 1;
if (is_dir(hfile)) {
is_cruft= 1;
}
if (!(dumpf_ok=holding_file_get_dumpfile(hfile, &dumpf)) ||
dumpf.type != F_DUMPFILE) {
if (dumpf_ok && dumpf.type == F_CONT_DUMPFILE) {
dumpfile_free_data(&dumpf);
continue; /* silently skip expected file */
}
is_cruft = 1;
}
if (dumpf.dumplevel < 0 || dumpf.dumplevel >= DUMP_LEVELS) {
is_cruft = 1;
}
if (per_file_fn)
proceed = per_file_fn(datap,
hdir,
workdir->d_name,
hfile,
is_cruft);
if (!is_cruft && proceed && stop_at != STOP_AT_FILE)
holding_walk_file(hfile,
datap,
per_chunk_fn);
dumpfile_free_data(&dumpf);
}
closedir(dir);
amfree(hfile);
}
/* Recurse over all holding directories in a holding disk.
*
* Call per_dir_fn for each dir, and so on, stopping at the level given by
* stop_at.
*
* datap is passed, unchanged, to all holding_walk_fns.
*
* @param hdisk: holding disk to examine (fully qualified path)
* @param datap: generic user-data pointer
* @param stop_at: do not proceed beyond this level of the hierarchy
* @param per_dir_fn: function to call for each holding dir
* @param per_file_fn: function to call for each holding file
* @param per_chunk_fn: function to call for each holding chunk
*/
static void
holding_walk_disk(
char *hdisk,
gpointer datap,
stop_at_t stop_at,
holding_walk_fn per_dir_fn,
holding_walk_fn per_file_fn,
holding_walk_fn per_chunk_fn)
{
DIR *dir;
struct dirent *workdir;
char *hdir = NULL;
int proceed = 1;
if ((dir = opendir(hdisk)) == NULL) {
if (errno != ENOENT)
dbprintf(_("Warning: could not open holding disk %s: %s\n"),
hdisk, strerror(errno));
return;
}
while ((workdir = readdir(dir)) != NULL) {
int is_cruft = 0;
if (is_dot_or_dotdot(workdir->d_name))
continue; /* expected cruft */
g_free(hdir);
hdir = g_strconcat(hdisk, "/", workdir->d_name, NULL);
/* detect cruft */
if (!is_dir(hdir)) {
is_cruft = 1;
} else if (!is_datestr(workdir->d_name)) {
/* EXT2/3 leave these in the root of each volume */
if (g_str_equal(workdir->d_name, "lost+found"))
continue; /* expected cruft */
else
is_cruft = 1; /* unexpected */
}
if (per_dir_fn) {
proceed = per_dir_fn(datap,
hdisk,
workdir->d_name,
hdir,
is_cruft);
}
if (!is_cruft && proceed && stop_at != STOP_AT_DIR) {
holding_walk_dir(hdir,
datap,
stop_at,
per_file_fn,
per_chunk_fn);
}
}
closedir(dir);
amfree(hdir);
}
/* Recurse over all holding disks.
*
* Call per_disk_fn for each disk, per_dir_fn for each dir, and so on, stopping
* at the level given by stop_at.
*
* datap is passed, unchanged, to all holding_walk_fns.
*
* @param datap: generic user-data pointer
* @param stop_at: do not proceed beyond this level of the hierarchy
* @param per_disk_fn: function to call for each holding disk
* @param per_dir_fn: function to call for each holding dir
* @param per_file_fn: function to call for each holding file
* @param per_chunk_fn: function to call for each holding chunk
*/
static void
holding_walk(
gpointer datap,
stop_at_t stop_at,
holding_walk_fn per_disk_fn,
holding_walk_fn per_dir_fn,
holding_walk_fn per_file_fn,
holding_walk_fn per_chunk_fn)
{
identlist_t il;
holdingdisk_t *hdisk_conf;
char *hdisk;
int proceed = 1;
for (il = getconf_identlist(CNF_HOLDINGDISK);
il != NULL;
il = il->next) {
hdisk_conf = lookup_holdingdisk(il->data);
hdisk = holdingdisk_get_diskdir(hdisk_conf);
if (per_disk_fn)
proceed = per_disk_fn(datap,
NULL,
hdisk,
hdisk,
0);
if (proceed && stop_at != STOP_AT_DISK)
holding_walk_disk(hdisk,
datap,
stop_at,
per_dir_fn,
per_file_fn,
per_chunk_fn);
}
}
/*
* holding_get_* functions
*/
typedef struct {
GSList *result;
int fullpaths;
int take_pid_lock;
} holding_get_datap_t;
/* Functor for holding_get_*; Stop if pid fileexists and is still alive
* the result.
*/
static int
holding_dir_stop_if_pid_fn(
gpointer datap,
char *hdisk G_GNUC_UNUSED,
char *element G_GNUC_UNUSED,
char *hdir,
int is_cruft)
{
holding_get_datap_t *data = (holding_get_datap_t *)datap;
if (is_cruft) {
return 0;
}
if (data->take_pid_lock) {
return take_holding_pid(hdir, getppid());
} else {
char *pid_file = g_strconcat(hdir, "/pid", NULL);
return can_take_holding(pid_file, 0);
}
}
/* Functor for holding_get_*; adds 'element' or 'fqpath' to
* the result.
*/
static int
holding_get_walk_fn(
gpointer datap,
G_GNUC_UNUSED char *base,
char *element,
char *fqpath,
int is_cruft)
{
holding_get_datap_t *data = (holding_get_datap_t *)datap;
int l;
/* ignore cruft */
if (is_cruft) return 0;
/* ignore tmp file */
if ((l = strlen(element)) >= 7 && g_str_has_prefix(&element[l - 4], ".tmp"))
return 0;
if (data->fullpaths)
data->result = g_slist_insert_sorted(data->result,
g_strdup(fqpath),
g_compare_strings);
else
data->result = g_slist_insert_sorted(data->result,
g_strdup(element),
g_compare_strings);
/* don't proceed any deeper */
return 0;
}
GSList *
holding_get_disks(void)
{
holding_get_datap_t data;
data.result = NULL;
data.fullpaths = 1; /* ignored anyway */
holding_walk((gpointer)&data,
STOP_AT_DISK,
holding_get_walk_fn, NULL, NULL, NULL);
return data.result;
}
GSList *
holding_get_files(
char *hdir,
int fullpaths,
int take_pid_lock)
{
holding_get_datap_t data;
data.result = NULL;
data.fullpaths = fullpaths;
data.take_pid_lock = take_pid_lock;
if (hdir) {
holding_walk_dir(hdir, (gpointer)&data,
STOP_AT_FILE,
holding_get_walk_fn, NULL);
} else {
holding_walk((gpointer)&data,
STOP_AT_FILE,
NULL, holding_dir_stop_if_pid_fn, holding_get_walk_fn, NULL);
}
return data.result;
}
GSList *
holding_get_file_chunks(char *hfile)
{
holding_get_datap_t data;
data.result = NULL;
data.fullpaths = 1;
data.take_pid_lock = 0;
holding_walk_file(hfile, (gpointer)&data,
holding_get_walk_fn);
return data.result;
}
GSList *
holding_get_files_for_flush(
GSList *dateargs)
{
GSList *file_list, *file_elt;
GSList *date;
int date_matches;
dumpfile_t file;
GSList *result_list = NULL;
/* loop over *all* files, checking each one's datestamp against the expressions
* in dateargs */
file_list = holding_get_files(NULL, 1, 1);
for (file_elt = file_list; file_elt != NULL; file_elt = file_elt->next) {
/* get info on that file */
if (!holding_file_get_dumpfile((char *)file_elt->data, &file))
continue;
if (file.type != F_DUMPFILE) {
dumpfile_free_data(&file);
continue;
}
if (dateargs) {
date_matches = 0;
/* loop over date args, until we find a match */
for (date = dateargs; date !=NULL; date = date->next) {
if (g_str_equal((char *)date->data, file.datestamp)) {
date_matches = 1;
break;
}
}
} else {
/* if no date list was provided, then all dates match */
date_matches = 1;
}
if (!date_matches) {
dumpfile_free_data(&file);
continue;
}
/* passed all tests -- we'll flush this file */
result_list = g_slist_insert_sorted(result_list,
g_strdup(file_elt->data),
g_compare_strings);
dumpfile_free_data(&file);
}
if (file_list) slist_free_full(file_list, g_free);
return result_list;
}
GSList *
holding_get_all_datestamps(void)
{
GSList *all_files, *file;
GSList *datestamps = NULL;
/* enumerate all files */
all_files = holding_get_files(NULL, 1, 0);
for (file = all_files; file != NULL; file = file->next) {
dumpfile_t dfile;
if (!holding_file_get_dumpfile((char *)file->data, &dfile))
continue;
if (!g_slist_find_custom(datestamps, dfile.datestamp,
g_compare_strings)) {
datestamps = g_slist_insert_sorted(datestamps,
g_strdup(dfile.datestamp),
g_compare_strings);
}
dumpfile_free_data(&dfile);
}
slist_free_full(all_files, g_free);
return datestamps;
}
off_t
holding_file_size(
char *hfile,
int strip_headers)
{
dumpfile_t file;
char *filename;
off_t size = (off_t)0;
struct stat finfo;
/* (note: we don't use holding_get_file_chunks here because that would
* entail opening each file twice) */
/* Loop through all cont_filenames (subsequent chunks) */
filename = g_strdup(hfile);
while (filename != NULL && filename[0] != '\0') {
/* stat the file for its size */
if (stat(filename, &finfo) == -1) {
dbprintf(_("stat %s: %s\n"), filename, strerror(errno));
size = -1;
break;
}
size += (finfo.st_size+(off_t)1023)/(off_t)1024;
if (strip_headers)
size -= (off_t)(DISK_BLOCK_BYTES / 1024);
/* get the header to look for cont_filename */
if (!holding_file_get_dumpfile(filename, &file)) {
dbprintf(_("holding_file_size: open of %s failed.\n"), filename);
size = -1;
break;
}
/* on to the next chunk */
g_free(filename);
filename = g_strdup(file.cont_filename);
dumpfile_free_data(&file);
}
amfree(filename);
return size;
}
int
holding_file_unlink(
char *hfile)
{
GSList *chunklist;
GSList *chunk;
chunklist = holding_get_file_chunks(hfile);
if (!chunklist)
return 0;
for (chunk = chunklist; chunk != NULL; chunk = chunk->next) {
if (unlink((char *)chunk->data)<0) {
dbprintf(_("holding_file_unlink: could not unlink %s: %s\n"),
(char *)chunk->data, strerror(errno));
slist_free_full(chunklist, g_free);
return 0;
}
}
slist_free_full(chunklist, g_free);
return 1;
}
int
holding_file_get_dumpfile(
char * fname,
dumpfile_t *file)
{
char buffer[DISK_BLOCK_BYTES];
int fd;
memset(buffer, 0, sizeof(buffer));
fh_init(file);
file->type = F_UNKNOWN;
if((fd = robust_open(fname, O_RDONLY, 0)) == -1)
return 0;
if(read_fully(fd, buffer, sizeof(buffer), NULL) != sizeof(buffer)) {
aclose(fd);
return 0;
}
aclose(fd);
parse_file_header(buffer, file, sizeof(buffer));
return 1;
}
/*
* Cleanup
*/
typedef struct {
corrupt_dle_fn corrupt_dle;
FILE *verbose_output;
} holding_cleanup_datap_t;
static int
holding_cleanup_disk(
gpointer datap,
G_GNUC_UNUSED char *base,
G_GNUC_UNUSED char *element,
char *fqpath,
int is_cruft)
{
holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
if (data->verbose_output) {
if (is_cruft)
g_fprintf(data->verbose_output,
_("Invalid holding disk '%s'\n"), fqpath);
else
g_fprintf(data->verbose_output,
_("Cleaning up holding disk '%s'\n"), fqpath);
}
return 1;
}
static int
holding_cleanup_dir(
gpointer datap,
G_GNUC_UNUSED char *base,
char *element,
char *fqpath,
int is_cruft)
{
holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
char *pid_file;
if (is_cruft) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("Invalid holding directory '%s'\n"), fqpath);
return 0;
}
/* Do not cleanup if not from us and their amdump is still running */
pid_file = g_strconcat(fqpath, "/pid", NULL);
if (!can_take_holding(pid_file, 1)) {
return 0;
}
g_free(pid_file);
/* try removing it */
if (rmdir(fqpath) == 0) {
/* success, so don't try to walk into it */
if (data->verbose_output)
g_fprintf(data->verbose_output,
_(" ..removed empty directory '%s'\n"), element);
return 0;
}
if (data->verbose_output)
g_fprintf(data->verbose_output,
_(" ..cleaning up holding directory '%s'\n"), element);
return 1;
}
static int
holding_cleanup_file(
gpointer datap,
G_GNUC_UNUSED char *base,
char *element,
char *fqpath,
int is_cruft)
{
holding_cleanup_datap_t *data = (holding_cleanup_datap_t *)datap;
int stat;
int l;
dumpfile_t file;
disk_t *dp;
if (is_cruft) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("Invalid holding file '%s'\n"), element);
return 0;
}
stat = holding_file_get_dumpfile(fqpath, &file);
if (!stat) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("Could not read read header from '%s'\n"), element);
dumpfile_free_data(&file);
return 0;
}
if (file.type != F_DUMPFILE && file.type != F_CONT_DUMPFILE) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("File '%s' is not a dump file\n"), element);
dumpfile_free_data(&file);
return 0;
}
if(file.dumplevel < 0 || file.dumplevel > 399) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("File '%s' has invalid level %d\n"), element, file.dumplevel);
dumpfile_free_data(&file);
return 0;
}
dp = lookup_disk(file.name, file.disk);
if (dp == NULL) {
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("File '%s' is for '%s:%s', which is not in the disklist\n"),
element, file.name, file.disk);
dumpfile_free_data(&file);
return 0;
}
if ((l = strlen(element)) >= 7 && g_str_has_prefix(&element[l - 4], ".tmp")) {
char *destname;
/* generate a name without '.tmp' */
destname = g_strdup(fqpath);
destname[strlen(destname) - 4] = '\0';
/* OK, it passes muster -- rename it to salvage some data,
* and mark the DLE as corrupted */
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("Processing partial holding file '%s'\n"), element);
if(rename_tmp_holding(destname, 0)) {
if (data->corrupt_dle)
data->corrupt_dle(dp->host->hostname, dp->name);
} else {
dbprintf(_("rename_tmp_holding(%s) failed\n"), destname);
if (data->verbose_output)
g_fprintf(data->verbose_output,
_("Rename of '%s' to '%s' failed.\n"), element, destname);
}
amfree(destname);
}
dumpfile_free_data(&file);
return 1;
}
void
holding_cleanup(
corrupt_dle_fn corrupt_dle,
FILE *verbose_output)
{
holding_cleanup_datap_t data;
data.corrupt_dle = corrupt_dle;
data.verbose_output = verbose_output;
holding_walk((gpointer)&data,
STOP_AT_FILE,
holding_cleanup_disk,
holding_cleanup_dir,
holding_cleanup_file,
NULL);
}
/*
* Application support
*/
void
holding_set_from_driver(
char *holding_file,
off_t orig_size,
crc_t native_crc,
crc_t client_crc,
crc_t server_crc)
{
int fd;
size_t buflen;
char buffer[DISK_BLOCK_BYTES];
char *read_buffer;
dumpfile_t file;
if((fd = robust_open(holding_file, O_RDWR, 0)) == -1) {
dbprintf(_("holding_set_origsize: open of %s failed: %s\n"),
holding_file, strerror(errno));
return;
}
buflen = read_fully(fd, buffer, sizeof(buffer), NULL);
if (buflen <= 0) {
dbprintf(_("holding_set_origsize: %s: empty file?\n"), holding_file);
close(fd);
return;
}
parse_file_header(buffer, &file, (size_t)buflen);
lseek(fd, (off_t)0, SEEK_SET);
file.orig_size = orig_size;
file.native_crc = native_crc;
file.client_crc = client_crc;
file.server_crc = server_crc;
read_buffer = build_header(&file, NULL, DISK_BLOCK_BYTES);
full_write(fd, read_buffer, DISK_BLOCK_BYTES);
dumpfile_free_data(&file);
amfree(read_buffer);
close(fd);
}
int
rename_tmp_holding(
char * holding_file,
int complete)
{
int fd;
size_t buflen;
char buffer[DISK_BLOCK_BYTES];
dumpfile_t file;
char *filename;
char *filename_tmp = NULL;
memset(buffer, 0, sizeof(buffer));
filename = g_strdup(holding_file);
while(filename != NULL && filename[0] != '\0') {
g_free(filename_tmp);
filename_tmp = g_strconcat(filename, ".tmp", NULL);
if((fd = robust_open(filename_tmp,O_RDONLY, 0)) == -1) {
dbprintf(_("rename_tmp_holding: open of %s failed: %s\n"),filename_tmp,strerror(errno));
amfree(filename);
amfree(filename_tmp);
return 0;
}
buflen = read_fully(fd, buffer, sizeof(buffer), NULL);
close(fd);
if(rename(filename_tmp, filename) != 0) {
dbprintf(_("rename_tmp_holding: could not rename \"%s\" to \"%s\": %s"),
filename_tmp, filename, strerror(errno));
}
if (buflen <= 0) {
dbprintf(_("rename_tmp_holding: %s: empty file?\n"), filename);
amfree(filename);
amfree(filename_tmp);
return 0;
}
parse_file_header(buffer, &file, (size_t)buflen);
if(complete == 0 ) {
char * header;
if((fd = robust_open(filename, O_RDWR, 0)) == -1) {
dbprintf(_("rename_tmp_holdingX: open of %s failed: %s\n"),
filename, strerror(errno));
dumpfile_free_data(&file);
amfree(filename);
amfree(filename_tmp);
return 0;
}
file.is_partial = 1;
if (debug_holding > 1)
dump_dumpfile_t(&file);
header = build_header(&file, NULL, DISK_BLOCK_BYTES);
if (!header) /* this shouldn't happen */
error(_("header does not fit in %zd bytes"), (size_t)DISK_BLOCK_BYTES);
if (full_write(fd, header, DISK_BLOCK_BYTES) != DISK_BLOCK_BYTES) {
dbprintf(_("rename_tmp_holding: writing new header failed: %s"),
strerror(errno));
dumpfile_free_data(&file);
amfree(filename);
amfree(filename_tmp);
free(header);
close(fd);
return 0;
}
free(header);
close(fd);
}
g_free(filename);
filename = g_strdup(file.cont_filename);
dumpfile_free_data(&file);
}
amfree(filename);
amfree(filename_tmp);
return 1;
}
int
mkholdingdir(
char * diskdir)
{
struct stat stat_hdp;
int success = 1;
char *pid_file;
FILE *pid_FILE;
if (mkpdir(diskdir, 0770, (uid_t)-1, (gid_t)-1) != 0 && errno != EEXIST) {
log_add(L_WARNING, _("WARNING: could not create parents of %s: %s"),
diskdir, strerror(errno));
success = 0;
}
else if (mkdir(diskdir, 0770) != 0 && errno != EEXIST) {
log_add(L_WARNING, _("WARNING: could not create %s: %s"),
diskdir, strerror(errno));
success = 0;
}
else if (stat(diskdir, &stat_hdp) == -1) {
log_add(L_WARNING, _("WARNING: could not stat %s: %s"),
diskdir, strerror(errno));
success = 0;
}
else {
if (!S_ISDIR((stat_hdp.st_mode))) {
log_add(L_WARNING, _("WARNING: %s is not a directory"),
diskdir);
success = 0;
}
else if (access(diskdir,W_OK) != 0) {
log_add(L_WARNING, _("WARNING: directory %s is not writable"),
diskdir);
success = 0;
}
}
/* create a 'pid' file */
pid_file = g_strconcat(diskdir, "/pid", NULL);
pid_FILE = fopen(pid_file, "wx");
if (!pid_FILE) {
log_add(L_WARNING, _("WARNING: Can't create '%s': %s"),
pid_file, strerror(errno));
success = 0;
} else {
fprintf(pid_FILE, "%d", (int)getpid());
fclose(pid_FILE);
}
g_free(pid_file);
return success;
}
/*
* return 0 - can't take
* 1 - can take
* 2 - already own
*/
static int can_take_holding(
char *pid_file,
int remove)
{
FILE *pid_FILE;
int result = 1;
pid_FILE = fopen(pid_file, "r");
if (pid_FILE) {
char line[1000];
int pid;
if (fgets(line, 1000, pid_FILE) != NULL) {
pid = atoi(line);
if (pid != getpid() && pid != getppid()) {
/* check if pid is alive */
if (kill(pid, 0) != -1) {
result = 0;
}
// remove pid file of dead process
unlink(pid_file);
} else {
if (remove) {
// remove my own pid file
unlink(pid_file);
} else {
result = 2;
}
}
}
fclose(pid_FILE);
}
return result;
}
gboolean take_holding_pid(char *diskdir, int pid);
gboolean
take_holding_pid(
char * diskdir,
int pid)
{
char *pid_file;
FILE *pid_FILE;
int result;
pid_file = g_strconcat(diskdir, "/pid", NULL);
result = can_take_holding(pid_file, 0);
if (result == 0) {
g_free(pid_file);
return 0;
} else if (result == 2) {
return 1;
}
/* create a 'pid' file */
pid_FILE = fopen(pid_file, "wx");
if (!pid_FILE) {
log_add(L_WARNING, _("WARNING: Can't create '%s': %s"),
pid_file, strerror(errno));
result = 0;
} else {
fprintf(pid_FILE, "%d", pid);
fclose(pid_FILE);
}
g_free(pid_file);
return result;
}