diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-08 19:23:42 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-02-08 19:23:42 +0200 |
commit | 90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7 (patch) | |
tree | 612823f0f3983e47cde17b549a8d85d5913cb0b3 /src/backup.c | |
parent | 0c6bb13d6b5df94417f2878cd5a4aa4ddcd78a39 (diff) | |
download | idest-90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7.tar.gz idest-90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7.tar.bz2 |
Preparation for implementing set/delete operations.
* src/backup.c: New file.
* gnulib.modules: Request backupfile, dirname and save-cwd.
* src/Makefile.am (idest_SOURCES): Add backup.c
* src/cmdline.opt: New options --backup and --backup-directory.
* src/id3v2.c: Minor change.
* src/idest.h: Include backupfile.h
(backup_type, backup_dir): New externs.
* src/main.c: Minor change.
Diffstat (limited to 'src/backup.c')
-rw-r--r-- | src/backup.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/backup.c b/src/backup.c new file mode 100644 index 0000000..495d6ac --- /dev/null +++ b/src/backup.c @@ -0,0 +1,220 @@ +/* This file is part of Idest. + Copyright (C) 2009 Sergey Poznyakoff + + Idest 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; either version 3, or (at your option) + any later version. + + Idest 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 Idest. If not, see <http://www.gnu.org/licenses/>. */ + +#include "idest.h" +#include <sys/stat.h> +#include <fcntl.h> +#include "save-cwd.h" +#include "dirname.h" +#include "backupfile.h" + +#define MKDIR_PERMISSIONS 0777 +#define CREAT_PERMISSIONS 0666 + +/* Concatenate BASE directory name, a single directory separator (/) and + NAME. Return in PBASELEN the length of BASE, not counting eventual trailing + slash. */ +char * +concat_dir(const char *base, const char *name, size_t *pbaselen) +{ + size_t len = strlen(base); + size_t size; + char *dir; + + while (len > 0 && base[len - 1] == '/') + len--; + + size = len + 1 + strlen(name); + dir = xmalloc(size + 1); + memcpy(dir, base, len); + dir[len++] = '/'; + strcpy(dir + len, name); + + if (pbaselen) + *pbaselen = len; + return dir; +} + +/* Create the directory DIR, eventually creating all intermediate directories + starting from DIR + BASELEN. */ +int +create_hierarchy(char *dir, size_t baselen) +{ + int rc; + struct stat st; + char *p; + + if (stat(dir, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + error(0, 0, "component %s is not a directory", dir); + return 1; + } + return 0; + } else if (errno != ENOENT) { + error(0, errno, "cannot stat file %s", dir); + return 1; + } + + p = strrchr(dir, '/'); + if (p) { + if (p - dir + 1 < baselen) { + error(0, 0, "base directory %s does not exist", dir); + return 1; + } + *p = 0; + } + + rc = create_hierarchy(dir, baselen); + if (rc == 0) { + if (p) + *p = '/'; + if (mkdir(dir, MKDIR_PERMISSIONS)) { + error(0, errno, "cannot create directory %s", dir); + rc = 1; + } + } + return rc; +} + +/* Create a directory BASE/NAME (with eventual intermediate directories in + NAME). */ +char * +create_directory(const char *base, const char *name) +{ + size_t baselen; + char *dir = concat_dir(base, name, &baselen); + + if (create_hierarchy(dir, baselen)) { + free(dir); + dir = NULL; + } + return dir; +} + + +/* Copy FILE to DST_FILE. */ +int +copy_file(const char *file, const char *dst_file) +{ + int in_fd, out_fd; + struct stat st; + char *buf = NULL; + size_t bufsize; + size_t fsize; + int rc; + + in_fd = open(file, O_RDONLY); + + if (in_fd == -1) { + error(0, errno, + "cannot open source file %s for reading", file); + return 1; + } + + if (fstat(in_fd, &st)) { + error(0, errno, "cannot stat source file %s", file); + close(in_fd); + return 1; + } + + out_fd = creat(dst_file, CREAT_PERMISSIONS); + if (out_fd == -1) { + error(0, errno, "cannot create destination file %s", file); + close(in_fd); + return 1; + } + + for (bufsize = fsize; + bufsize > 0 && (buf = malloc(bufsize)) == NULL; + bufsize /= 2); + if (bufsize == 0) + xalloc_die(); + + rc = 0; + fsize = st.st_size; + while (fsize > 0) { + size_t rest; + size_t rdbytes; + + rest = fsize > bufsize ? bufsize : fsize; + rdbytes = read(in_fd, buf, rest); + if (rdbytes == -1) { + error(0, errno, "unexpected error reading %s", file); + rc = 1; + break; + } + rest = write(out_fd, buf, rdbytes); + if (rest == -1) { + error(0, errno, "unexpected error writing to %s", + dst_file); + rc = 1; + break; + } else if (rest != rdbytes) { + error(0, 0, "short write on %s", dst_file); + rc = 1; + } + fsize -= rdbytes; + } + free(buf); + + close(in_fd); + close(out_fd); + if (rc) { + unlink(dst_file); + return 1; + } + return 0; +} + +/* Backup a file */ +int +backup_file(const char *file) +{ + int rc = 0; + char *adir; + char *new_name; + + if (backup_dir) { + if (file[0] == '/') + new_name = concat_dir(backup_dir, + last_component(file), NULL); + else + new_name = concat_dir(backup_dir, file, NULL); + if (access(new_name, F_OK) == 0) { + if (backup_type == no_backups) { + if (unlink(new_name)) + error(0, errno, + "cannot unlink backup file %s", + new_name); + /* Try to continue anyway */ + } else { + char *bfn = find_backup_file_name(new_name, + backup_type); + copy_file(new_name, bfn); + /* Continue no matter what the result of + copy_file was */ + } + } + } else if (backup_type == no_backups) + return 0; + else + new_name = find_backup_file_name(file, backup_type); + + rc = copy_file(file, new_name); + free(new_name); + return rc; +} + |