--- tdl-1.5.2.orig/main.c +++ tdl-1.5.2/main.c @@ -80,7 +80,7 @@ return; } /*}}}*/ -static volatile void unlock_and_exit(int code)/*{{{*/ +static void unlock_and_exit(int code)/*{{{*/ { unlock_database(); exit(code); @@ -237,22 +237,91 @@ } /*}}}*/ -static void rename_database(char *path)/*{{{*/ +static mode_t get_mode(const char *path); /* prototype */ +/*}}}*/ +static int copy_file_contents(char *pathsrc, char *pathdest) { + int src, dest; + ssize_t rdsize = 1; + char buf[4096]; + + src = open(pathsrc, O_RDONLY); + if (src == -1) { + perror("warning, couldn't open database"); + return 0; + } + dest = open(pathdest, O_WRONLY | O_CREAT, get_mode(pathsrc)); + if (dest == -1) { + perror("warning, couldn't open/create backup database"); + close(src); + return 0; + } + if (ftruncate(dest,0) != 0) { + perror("warning, couldn't truncate backup database"); + close(src); + close(dest); + return 0; + } + lseek(src,0,SEEK_SET); + lseek(dest,0,SEEK_SET); + while (rdsize > 0) { + rdsize = read(src, buf, 4096); + if (rdsize == -1) { + perror("warning, error reading database"); + close(src); + close(dest); + return 0; + } + if (rdsize > 0) { + if (write(dest, buf, rdsize) != rdsize) { + perror("warning, error writing to backup database"); + close(src); + close(dest); + return 0; + } + } + } + close(src); + close(dest); + return 1; +} +/*}}}*/ +static int path_is_symlink(char *path) { + int i; + struct stat s; + i = lstat(path, &s); + if ((i == 0) && (S_ISLNK(s.st_mode))) { + return 1; /* is a symlink */ + } + return 0; /* not a symlink */ +} +/*}}}*/ +static int rename_database(char *path)/*{{{*/ { - int len; + /* the rename_database function returns 1 if database or/and + * database backup file are symlinks; otherwise returns 0 */ + int len, symlinks; char *pathbak; - + len = strlen(path); pathbak = new_array(char, len + 5); strcpy(pathbak, path); strcat(pathbak, ".bak"); - if (rename(path, pathbak) < 0) { - if (is_noisy) { - perror("warning, couldn't save backup database:"); + + symlinks = path_is_symlink(path) | path_is_symlink(pathbak); + + if (symlinks) { + if (access(path,F_OK) == 0) { + copy_file_contents(path, pathbak); + } + } else { + if (rename(path, pathbak) < 0) { + if (is_noisy) { + perror("warning, couldn't save backup database:"); + } } } free(pathbak); - return; + return symlinks; } /*}}}*/ static char *executable_name(char *argv0)/*{{{*/ @@ -315,7 +384,7 @@ /*}}}*/ static void save_database(char *path)/*{{{*/ { - FILE *out; + FILE *out = NULL; int out_fd; mode_t database_mode; if (is_loaded && currently_dirty) { @@ -324,20 +393,34 @@ /* The next line only used to happen if the command wasn't 'create'. * However, it should quietly fail for create, where the existing database * doesn't exist */ - rename_database(path); - - /* Open database this way so that the permissions from the existing - database can be duplicated onto the new one in way free of race - conditions. */ - out_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, database_mode); - if (out_fd < 0) { - fprintf(stderr, "Could not open new database %s for writing : %s\n", - path, strerror(errno)); - unlock_and_exit(1); + if (rename_database(path) == 0) { + /* database is a regular file */ + /* Open database this way so that the permissions from the existing + database can be duplicated onto the new one in way free of race + conditions. */ + out_fd = open(path, O_WRONLY | O_CREAT | O_EXCL, database_mode); + if (out_fd < 0) { + fprintf(stderr, "Could not open new database %s for writing : %s\n", + path, strerror(errno)); + unlock_and_exit(1); + } } else { - /* Normal case */ - out = fdopen(out_fd, "wb"); + /* database and/or backup database are symlinks */ + /* we should truncate existing file and write its contents */ + out_fd = open(path, O_WRONLY | O_CREAT, database_mode); + if (out_fd < 0) { + fprintf(stderr, "Could not open database %s for writing : %s\n", + path, strerror(errno)); + unlock_and_exit(1); + } else { + /* Normal case */ + if (ftruncate(out_fd, 0) != 0) { + perror("warning, couldn't truncate database:"); + unlock_and_exit(1); + } + } } + out = fdopen(out_fd, "wb"); if (!out) { fprintf(stderr, "Cannot open database %s for writing\n", path); unlock_and_exit(1); @@ -728,6 +811,11 @@ if (!is_loaded && cmds[index].load_db) { load_database(current_database_path); + if (is_interactive && (!is_loaded)) { + fprintf(stderr, "error: could not open database. please create a " + "database with 'tdl create' before using this tdl command\n"); + unlock_and_exit(-1); + } } pp = is_tdl ? (p + 1) : p;