void ls_switch_file( ChannelLog *cl ) { static char tmbuf[MAXPATH]; static char newfname[MAXPATH]; static char oldfname[MAXPATH]; int res; /* no need to switch, its not opened */ if( cl->logfile == NULL ) return; ls_close_log( cl ); /* check if the target directory exists */ if( os_create_dir( LogServ.savedir ) != NS_SUCCESS ) { return; } os_strftime( tmbuf, MAXPATH, "%d%m%Y%H%M%S", os_localtime( &me.now ) ); ircsnprintf( newfname, MAXPATH, "%s/%s-%s.log", LogServ.savedir, cl->filename, tmbuf ); ircsnprintf( oldfname, MAXPATH, "%s/%s.log", LogServ.logdir, cl->filename ); res = os_rename( oldfname, newfname ); if( res != 0 ) { nlog( LOG_CRITICAL, "Couldn't rename file %s: %s", oldfname, os_strerror() ); return; } nlog( LOG_NORMAL, "Switched Logfile for %s from %s to %s", cl->channame, oldfname, newfname ); }
bool os_quick_write_utf8_file_safe(const char *path, const char *str, size_t len, bool marker, const char *temp_ext, const char *backup_ext) { struct dstr backup_path = {0}; struct dstr temp_path = {0}; bool success = false; if (!temp_ext || !*temp_ext) { blog(LOG_ERROR, "os_quick_write_utf8_file_safe: invalid " "temporary extension specified"); return false; } dstr_copy(&temp_path, path); if (*temp_ext != '.') dstr_cat(&temp_path, "."); dstr_cat(&temp_path, temp_ext); if (!os_quick_write_utf8_file(temp_path.array, str, len, marker)) { goto cleanup; } if (backup_ext && *backup_ext) { dstr_copy(&backup_path, path); if (*backup_ext != '.') dstr_cat(&backup_path, "."); dstr_cat(&backup_path, backup_ext); os_unlink(backup_path.array); os_rename(path, backup_path.array); dstr_free(&backup_path); } else { os_unlink(path); } os_rename(temp_path.array, path); success = true; cleanup: dstr_free(&backup_path); dstr_free(&temp_path); return success; }
static inline void replace_file(const char *src_base_path, const char *dst_base_path, const char *file) { char *src_path = get_path(src_base_path, file); char *dst_path = get_path(dst_base_path, file); if (src_path && dst_path) { os_unlink(dst_path); os_rename(src_path, dst_path); } bfree(dst_path); bfree(src_path); }
static void copy_local_to_cache(struct update_info *info, const char *file) { char *local_file_path = get_path(info->local, file); char *cache_file_path = get_path(info->cache, file); char *temp_file_path = get_path(info->temp, file); os_copyfile(local_file_path, temp_file_path); os_unlink(cache_file_path); os_rename(temp_file_path, cache_file_path); bfree(local_file_path); bfree(cache_file_path); bfree(temp_file_path); }
/* Implementation of traverse() visitor for subtree removal. Returns 0 on * success, otherwise non-zero is returned. */ static VisitResult rm_visitor(const char full_path[], VisitAction action, void *param) { io_args_t *const rm_args = param; VisitResult result = VR_OK; if(rm_args->cancellable && ui_cancellation_requested()) { return VR_CANCELLED; } switch(action) { case VA_DIR_ENTER: /* Do nothing, directories are removed on leaving them. */ result = VR_OK; break; case VA_FILE: { io_args_t args = { .arg1.path = full_path, .cancellable = rm_args->cancellable, .estim = rm_args->estim, .result = rm_args->result, }; result = (iop_rmfile(&args) == 0) ? VR_OK : VR_ERROR; rm_args->result = args.result; break; } case VA_DIR_LEAVE: { io_args_t args = { .arg1.path = full_path, .cancellable = rm_args->cancellable, .estim = rm_args->estim, .result = rm_args->result, }; result = (iop_rmdir(&args) == 0) ? VR_OK : VR_ERROR; rm_args->result = args.result; break; } } return result; } int ior_cp(io_args_t *const args) { const char *const src = args->arg1.src; const char *const dst = args->arg2.dst; if(is_in_subtree(dst, src)) { (void)ioe_errlst_append(&args->result.errors, src, IO_ERR_UNKNOWN, "Can't copy parent path into subpath"); return 1; } if(args->arg3.crs == IO_CRS_REPLACE_ALL) { io_args_t rm_args = { .arg1.path = dst, .cancellable = args->cancellable, .estim = args->estim, .result = args->result, }; const int result = ior_rm(&rm_args); args->result = rm_args.result; if(result != 0) { if(!args->cancellable || !ui_cancellation_requested()) { (void)ioe_errlst_append(&args->result.errors, dst, IO_ERR_UNKNOWN, "Failed to remove"); } return result; } } return traverse(src, &cp_visitor, args); } /* Implementation of traverse() visitor for subtree copying. Returns 0 on * success, otherwise non-zero is returned. */ static VisitResult cp_visitor(const char full_path[], VisitAction action, void *param) { return cp_mv_visitor(full_path, action, param, 1); } int ior_mv(io_args_t *const args) { const char *const src = args->arg1.src; const char *const dst = args->arg2.dst; const IoCrs crs = args->arg3.crs; const io_confirm confirm = args->confirm; if(crs == IO_CRS_FAIL && path_exists(dst, DEREF) && !is_case_change(src, dst)) { (void)ioe_errlst_append(&args->result.errors, dst, EEXIST, strerror(EEXIST)); return 1; } if(crs == IO_CRS_APPEND_TO_FILES) { if(!is_file(src)) { (void)ioe_errlst_append(&args->result.errors, src, EISDIR, strerror(EISDIR)); return 1; } if(!is_file(dst)) { (void)ioe_errlst_append(&args->result.errors, dst, EISDIR, strerror(EISDIR)); return 1; } } else if(crs == IO_CRS_REPLACE_FILES && path_exists(dst, DEREF)) { /* Ask user whether to overwrite destination file. */ if(confirm != NULL && !confirm(args, src, dst)) { return 0; } } if(os_rename(src, dst) == 0) { ioeta_update(args->estim, src, dst, 1, 0); return 0; } switch(errno) { case EXDEV: { int result = ior_cp(args); if(result == 0) { io_args_t rm_args = { .arg1.path = src, .cancellable = args->cancellable, .estim = args->estim, .result = args->result, }; /* Disable progress reporting for this "secondary" operation. */ const int silent = ioeta_silent_on(rm_args.estim); result = ior_rm(&rm_args); args->result = rm_args.result; ioeta_silent_set(rm_args.estim, silent); } return result; } case EISDIR: case ENOTEMPTY: case EEXIST: #ifdef _WIN32 /* For MXE builds running in Wine. */ case EPERM: case EACCES: #endif if(crs == IO_CRS_REPLACE_ALL) { int error; io_args_t rm_args = { .arg1.path = dst, .cancellable = args->cancellable, .estim = args->estim, .result = args->result, }; /* Ask user whether to overwrite destination file. */ if(confirm != NULL && !confirm(args, src, dst)) { return 0; } error = ior_rm(&rm_args); args->result = rm_args.result; if(error != 0) { if(!args->cancellable || !ui_cancellation_requested()) { (void)ioe_errlst_append(&args->result.errors, dst, IO_ERR_UNKNOWN, "Failed to remove"); } return error; } if(os_rename(src, dst) != 0) { (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return 1; } return 0; } else if(crs == IO_CRS_REPLACE_FILES || (!has_atomic_file_replace() && crs == IO_CRS_APPEND_TO_FILES)) { if(!has_atomic_file_replace() && is_file(dst)) { io_args_t rm_args = { .arg1.path = dst, .cancellable = args->cancellable, .estim = args->estim, .result = args->result, }; const int error = iop_rmfile(&rm_args); args->result = rm_args.result; if(error != 0) { if(!args->cancellable || !ui_cancellation_requested()) { (void)ioe_errlst_append(&args->result.errors, dst, IO_ERR_UNKNOWN, "Failed to remove"); } return error; } } return traverse(src, &mv_visitor, args); } /* Break is intentionally omitted. */ default: (void)ioe_errlst_append(&args->result.errors, src, errno, strerror(errno)); return errno; } } /* Checks that path points to a file or symbolic link. Returns non-zero if so, * otherwise zero is returned. */ static int is_file(const char path[]) { return !is_dir(path) || (is_symlink(path) && get_symlink_type(path) != SLT_UNKNOWN); } /* Implementation of traverse() visitor for subtree moving. Returns 0 on * success, otherwise non-zero is returned. */ static VisitResult mv_visitor(const char full_path[], VisitAction action, void *param) { return cp_mv_visitor(full_path, action, param, 0); } /* Generic implementation of traverse() visitor for subtree copying/moving. * Returns 0 on success, otherwise non-zero is returned. */ static VisitResult cp_mv_visitor(const char full_path[], VisitAction action, void *param, int cp) { io_args_t *const cp_args = param; const char *dst_full_path; char *free_me = NULL; VisitResult result = VR_OK; const char *rel_part; if(cp_args->cancellable && ui_cancellation_requested()) { return VR_CANCELLED; } /* TODO: come up with something better than this. */ rel_part = full_path + strlen(cp_args->arg1.src); dst_full_path = (rel_part[0] == '\0') ? cp_args->arg2.dst : (free_me = format_str("%s/%s", cp_args->arg2.dst, rel_part)); switch(action) { case VA_DIR_ENTER: if(cp_args->arg3.crs != IO_CRS_REPLACE_FILES || !is_dir(dst_full_path)) { io_args_t args = { .arg1.path = dst_full_path, /* Temporary fake rights so we can add files to the directory. */ .arg3.mode = 0700, .cancellable = cp_args->cancellable, .estim = cp_args->estim, .result = cp_args->result, }; result = (iop_mkdir(&args) == 0) ? VR_OK : VR_ERROR; cp_args->result = args.result; } break; case VA_FILE: { io_args_t args = { .arg1.src = full_path, .arg2.dst = dst_full_path, .arg3.crs = cp_args->arg3.crs, /* It's safe to always use fast file cloning on moving files. */ .arg4.fast_file_cloning = cp ? cp_args->arg4.fast_file_cloning : 1, .cancellable = cp_args->cancellable, .confirm = cp_args->confirm, .estim = cp_args->estim, .result = cp_args->result, }; result = ((cp ? iop_cp(&args) : ior_mv(&args)) == 0) ? VR_OK : VR_ERROR; cp_args->result = args.result; break; } case VA_DIR_LEAVE: { struct stat st; if(cp_args->arg3.crs == IO_CRS_REPLACE_FILES && !cp) { io_args_t rm_args = { .arg1.path = full_path, .cancellable = cp_args->cancellable, .estim = cp_args->estim, .result = cp_args->result, }; result = (iop_rmdir(&rm_args) == 0) ? VR_OK : VR_ERROR; } else if(os_stat(full_path, &st) == 0) { result = (os_chmod(dst_full_path, st.st_mode & 07777) == 0) ? VR_OK : VR_ERROR; if(result == VR_ERROR) { (void)ioe_errlst_append(&cp_args->result.errors, dst_full_path, errno, strerror(errno)); } clone_timestamps(dst_full_path, full_path, &st); } else { (void)ioe_errlst_append(&cp_args->result.errors, full_path, errno, strerror(errno)); result = VR_ERROR; } break; } } free(free_me); return result; }