aboutsummaryrefslogtreecommitdiff
path: root/src/backup.c
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2009-02-08 19:23:42 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2009-02-08 19:23:42 +0200
commit90ea6a2cf2e646441b2c2ea4bf480fe8e4152de7 (patch)
tree612823f0f3983e47cde17b549a8d85d5913cb0b3 /src/backup.c
parent0c6bb13d6b5df94417f2878cd5a4aa4ddcd78a39 (diff)
downloadidest-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.c220
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;
+}
+

Return to:

Send suggestions and report system problems to the System administrator.