/* * Copyright (C) 2011, 2012, 2014 J. Ali Harlow * * 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; either version 2 of the License, or * (at your option) any later version. * * 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, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #if !ENABLE_ATOMIC #include #ifdef MSWIN_API #include #endif #include #include #include #include #include #include #include #include #include "razor.h" #include "razor-internal.h" #ifndef O_BINARY #define O_BINARY 0 #endif RAZOR_EXPORT struct razor_atomic * razor_atomic_open(const char *description) { struct razor_atomic *atomic; atomic = zalloc(sizeof *atomic); return atomic; } RAZOR_EXPORT int razor_atomic_commit(struct razor_atomic *atomic) { return razor_atomic_in_error_state(atomic); } RAZOR_EXPORT void razor_atomic_destroy(struct razor_atomic *atomic) { if (atomic->error) razor_error_free(atomic->error); free(atomic); } RAZOR_EXPORT int razor_atomic_make_dirs(struct razor_atomic *atomic, const char *root, const char *path) { char buffer[PATH_MAX], *p; const char *slash, *next; struct stat buf; if (razor_atomic_in_error_state(atomic)) return -1; strcpy(buffer, root); p = buffer + strlen(buffer); slash = (p > buffer) ? SKIP_DRIVE_PATH(path) : path; for (slash = path; *slash != '\0'; slash = next) { #ifdef MSWIN_API next = strpbrk(slash + 1, "/\\"); #else next = strchr(slash + 1, '/'); #endif if (next == NULL) break; memcpy(p, slash, next - slash); p += next - slash; *p = '\0'; if (razor_valid_root_name(buffer)) continue; if (stat(buffer, &buf) == 0) { if (!S_ISDIR(buf.st_mode)) { atomic->error = razor_error_new_str(buffer, "Not a directory"); return -1; } } else if (mkdir(buffer, 0777) < 0) { atomic->error = razor_error_new_str(buffer, strerror(errno)); return -1; } } return 0; } RAZOR_EXPORT int razor_atomic_remove(struct razor_atomic *atomic, const char *path) { #ifdef MSWIN_API wchar_t *buf; DWORD err; #endif if (razor_atomic_in_error_state(atomic)) return -1; #ifdef MSWIN_API buf = razor_utf8_to_utf16(path, -1); if (!DeleteFileW(buf)) { err = GetLastError(); if (err != ERROR_FILE_NOT_FOUND && err != ERROR_PATH_NOT_FOUND && !(SetFileAttributesW(buf, FILE_ATTRIBUTE_NORMAL) && DeleteFileW(buf)) && !RemoveDirectoryW(buf) && GetLastError() != ERROR_DIR_NOT_EMPTY) atomic->error = razor_error_new_mswin(buf, err); } free(buf); #else if (remove(path)) atomic->error = razor_error_new_str(path, strerror(errno)); #endif return razor_atomic_in_error_state(atomic); } RAZOR_EXPORT int razor_atomic_rename_file(struct razor_atomic *atomic, const char *oldpath, const char *newpath) { #ifdef MSWIN_API wchar_t *oldbuf, *newbuf; const DWORD flags = MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING; #endif if (razor_atomic_in_error_state(atomic)) return -1; #ifdef MSWIN_API newbuf = razor_utf8_to_utf16(newpath, -1); oldbuf = razor_utf8_to_utf16(oldpath, -1); /* * Passing MOVEFILE_REPLACE_EXISTING to MoveFileEx() will * cover every case we care about _except_ replacing an empty * directory with a file. Calling RemoveDirectory() will deal * with this case while having no effect in all other cases. */ (void)RemoveDirectoryW(newbuf); if (!MoveFileExW(oldbuf, newbuf, flags)) atomic->error = razor_error_new_mswin(newbuf, GetLastError()); free(newbuf); free(oldbuf); #else if (rename(oldpath, newpath)) atomic->error = razor_error_new_str(newpath, strerror(errno)); #endif return razor_atomic_in_error_state(atomic); } RAZOR_EXPORT int razor_atomic_create_dir(struct razor_atomic *atomic, const char *dirname, mode_t mode) { if (razor_atomic_in_error_state(atomic)) return -1; if (!mkdir(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO))) return 0; if (errno != EEXIST) { atomic->error = razor_error_new_str(dirname, strerror(errno)); return -1; } if (chmod(dirname, mode & (S_IRWXU | S_IRWXG | S_IRWXO)) < 0) { atomic->error = razor_error_new_str(dirname, strerror(errno)); return -1; } return 0; } RAZOR_EXPORT int razor_atomic_create_symlink(struct razor_atomic *atomic, const char *target, const char *path) { if (razor_atomic_in_error_state(atomic)) return -1; #if HAVE_SYMLINK if (symlink(target, path) < 0) { atomic->error = razor_error_new_str(path, strerror(errno)); return -1; } return 0; #else atomic->error = razor_error_new_str(NULL, "Symbolic links not supported " "on this platform"); return -1; #endif } RAZOR_EXPORT int razor_atomic_create_file(struct razor_atomic *atomic, const char *filename, mode_t mode) { int fd; if (razor_atomic_in_error_state(atomic)) return -1; fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, mode & (S_IRWXU | S_IRWXG | S_IRWXO)); if (fd == -1) atomic->error = razor_error_new_str(filename, strerror(errno)); return fd; } #endif /* !ENABLE_ATOMIC */