Files
vimdir/source/file_utils.c
2025-01-30 13:44:57 +01:00

352 lines
9.6 KiB
C

#include "file_utils.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include "error.h"
#include "remove_all.h"
extern char * trim_trailing_slashes(char * path) {
int len = strlen(path);
while (len > 1
&& path[len-1] == '/') {
path[len-1] = '\0';
--len;
}
return path;
}
static const char * custom_rm = NULL;
int (*mytouch)(const char *filename) = NULL;
int (*mydelete)(const char *filename) = NULL;
int (*mychmod)(const char *filename, mode_t mode) = NULL;
int (*mychown)(const char *filename, const char *owner, const char *group) = NULL;
int (*mymove)(const char *filename, const char *newname) = NULL;
int (*mycopy)(const char *filename, const char *newname) = NULL;
move_data_t (*mytempmove)(const char *filename, const char *newname) = NULL;
static int dry_touch(const char * filename);
static int dry_delete(const char * filename);
static int dry_chmod(const char * filename, mode_t mode);
static int dry_chown(const char * filename, const char * owner, const char * group);
static int dry_move(const char * filename, const char * newname);
static int dry_copy(const char * filename, const char * newname);
static move_data_t dry_mytempmove(const char *filename, const char *newname);
static int moist_touch(const char * filename);
static int moist_delete(const char * filename);
static int moist_chmod(const char * filename, mode_t mode);
static int moist_chown(const char * filename, const char * owner, const char * group);
static int moist_move(const char * filename, const char * newname);
static int moist_copy(const char * filename, const char * newname);
static move_data_t moist_mytempmove(const char *filename, const char *newname);
int init_file_utils(bool is_dry_run, const char * custom_rm_) {
custom_rm = custom_rm_;
if (is_dry_run) {
mytouch = dry_touch;
mydelete = dry_delete;
mychmod = dry_chmod;
mychown = dry_chown;
mymove = dry_move;
mycopy = dry_copy;
mytempmove = dry_mytempmove;
} else {
mytouch = moist_touch;
mydelete = moist_delete;
mychmod = moist_chmod;
mychown = moist_chown;
mymove = moist_move;
mycopy = moist_copy;
mytempmove = moist_mytempmove;
}
return 0;
}
int deinit_file_utis() {
mytouch = NULL;
mydelete = NULL;
mychmod = NULL;
mychown = NULL;
mymove = NULL;
custom_rm = NULL;
return 0;
}
char mode_type_to_char(mode_t m) {
switch (m & S_IFMT) {
case S_IFREG: return '-'; // regular file
case S_IFDIR: return 'd'; // directory
case S_IFCHR: return 'c'; // character device
case S_IFBLK: return 'b'; // block device
case S_IFIFO: return 'p'; // fifo (pipe)
case S_IFLNK: return 'l'; // symbolic link
case S_IFSOCK: return 's'; // socket
default: return '?'; // unknown
}
}
mode_t char_to_mode_type(const char c) {
switch (c) {
case '-': return S_IFREG; // regular file
case 'd': return S_IFDIR; // directory
case 'c': return S_IFCHR; // character device
case 'b': return S_IFBLK; // block device
case 'p': return S_IFIFO; // fifo (pipe)
case 'l': return S_IFLNK; // symbolic link
case 's': return S_IFSOCK; // socket
default: return 0; // unknown
}
}
char * mode_to_str(mode_t mode, char * buffer) {
buffer[0] = mode_type_to_char(mode);
buffer[1] = (mode & S_IRUSR) ? 'r' : '-';
buffer[2] = (mode & S_IWUSR) ? 'w' : '-';
buffer[3] = (mode & S_IXUSR) ? 'x' : '-';
buffer[4] = (mode & S_IRGRP) ? 'r' : '-';
buffer[5] = (mode & S_IWGRP) ? 'w' : '-';
buffer[6] = (mode & S_IXGRP) ? 'x' : '-';
buffer[7] = (mode & S_IROTH) ? 'r' : '-';
buffer[8] = (mode & S_IWOTH) ? 'w' : '-';
buffer[9] = (mode & S_IXOTH) ? 'x' : '-';
buffer[10] = '\0';
return buffer;
}
mode_t str_to_mode(const char *permissions) {
mode_t mode = 0;
mode |= char_to_mode_type(permissions[0]);
mode |= (permissions[1] == 'r') ? S_IRUSR : 0;
mode |= (permissions[2] == 'w') ? S_IWUSR : 0;
mode |= (permissions[3] == 'x') ? S_IXUSR : 0;
mode |= (permissions[4] == 'r') ? S_IRGRP : 0;
mode |= (permissions[5] == 'w') ? S_IWGRP : 0;
mode |= (permissions[6] == 'x') ? S_IXGRP : 0;
mode |= (permissions[7] == 'r') ? S_IROTH : 0;
mode |= (permissions[8] == 'w') ? S_IWOTH : 0;
mode |= (permissions[9] == 'x') ? S_IXOTH : 0;
return mode;
}
// --- Dry implementations
static
int dry_touch(const char * filename) {
size_t len = strlen(filename);
if (filename[len-1] != '/' ) {
notice("touch '%s' (dry; subsequent stats will fail)", filename);
} else {
notice("mkdir '%s' (dry; subsequent stats will fail)", filename);
}
return 0;
}
static
int dry_delete(const char * filename) {
notice("delete '%s'", filename);
return 0;
}
static
int dry_chmod(const char * filename, mode_t mode) {
char buf[11];
notice("chmod '%s' (%s)", filename, mode_to_str(mode, buf));
return 0;
}
static
int dry_chown(const char * filename, const char * owner, const char * group) {
notice("chown '%s' (%s:%s)", filename, owner, group);
return 0;
}
static
int dry_move(const char * filename, const char * newname) {
notice("rename '%s' (-> '%s')", filename, newname);
return 0;
}
static
int dry_copy(const char * filename, const char * newname) {
notice("copy '%s' (as '%s')", filename, newname);
return 0;
}
static
move_data_t dry_mytempmove(const char * filename, const char * newname) {
notice("swap detected in a dry-run ('%s' <-> '%s'); the following logs will be inaccurate", filename, newname);
return (move_data_t) {
.orig_name = strdup(filename),
.curt_name = strdup(filename),
.dest_name = strdup(newname),
};
}
// --- Moist implementations
static
int moist_touch(const char * filename) {
size_t len = strlen(filename);
if (filename[len-1] != '/' ) {
FILE * f = fopen(filename, "w");
CHECK_OPEN(f, filename, return 1);
fclose(f);
} else {
mkdir(filename, 0777);
}
return 0;
}
static
int moist_delete(const char * filename) {
/* Theres the situation where the user attempts
* to delete a recursively listed directory.
* He would delete all references to the directory
* (otherwise it would be a stat error too).
* Since the entry came somewhere, its reasonably safe to assume the file should exist,
* and if it does not, its not an actual error.
* Therefor, we simply make deletes on a missing file a nop.
*/
if (access(filename, F_OK)) { return 0; }
if (custom_rm) {
size_t cmd_len = strlen(custom_rm)
+ sizeof(' ')
+ sizeof('\'')*2
+ strlen(filename)
+ 1
;
char cmd[cmd_len];
snprintf(cmd, cmd_len, "%s '%s'", custom_rm, filename);
int result = system(cmd);
if (result == 127
|| result == -1
|| (WIFEXITED(result) && WEXITSTATUS(result) != 0)) {
errorn(E_FILE_DELETE, filename);
return 1;
}
} else {
if (remove_all(filename)) {
errorn(E_FILE_DELETE, filename);
return 1;
}
}
return 0;
}
static
int moist_chmod(const char * filename, mode_t mode) {
if (chmod(filename, mode) != 0) {
errorn(E_FILE_ACCESS, filename);
return 1;
}
return 0;
}
static
int moist_chown(const char * filename, const char * owner, const char * group) {
uid_t uid = -1;
gid_t gid = -1;
struct passwd * usr = getpwnam(owner);
if (!usr) {
errorn(E_NO_USER, owner);
return 1;
}
uid = usr->pw_uid;
struct group * grp = getgrnam(group);
if (!grp) {
errorn(E_NO_GROUP, group);
return 1;
}
gid = grp->gr_gid;
if (chown(filename, uid, gid)) {
errorn(E_FILE_CHOWN, filename);
return 1;
}
return 0;
}
static
int moist_move(const char * filename, const char * newname) {
if (rename(filename, newname) != 0) {
errorn(E_FILE_MOVE, filename, newname);
return 1;
}
return 0;
}
static
int moist_copy(const char * filename, const char * newname) {
// Is using system for copying terrible? yes.
// Do I have know a better solution thats not filled with footguns? no.
size_t cmd_len = strlen("cp -a")
+ sizeof(' ') + sizeof('\'')*2 + strlen(filename)
+ sizeof(' ') + sizeof('\'')*2 + strlen(newname)
+ 1
;
char cmd[cmd_len];
snprintf(cmd, cmd_len, "cp -a '%s' '%s'", filename, newname);
int result = system(cmd);
if (result == 127
|| result == -1
|| (WIFEXITED(result) && WEXITSTATUS(result) != 0)) {
errorn(E_FILE_COPY, filename, newname);
return 1;
}
return 0;
}
static
move_data_t moist_mytempmove(const char * filename, const char * newname) {
move_data_t r = {
.orig_name = NULL,
.curt_name = NULL,
.dest_name = NULL,
};
const int COLISION_DIGITS = 3;
const size_t buf_size = strlen(filename) + COLISION_DIGITS + sizeof("~");
char buffer[buf_size];
unsigned n = 0;
do {
snprintf(buffer, buf_size, "%s~%d", filename, n++);
if (n > 10 * COLISION_DIGITS) { goto end; }
} while (!access(buffer, F_OK));
if (mymove(filename, buffer)) { goto end; }
r.orig_name = strdup(filename);
r.curt_name = strdup(buffer);
r.dest_name = strdup(newname);
end:
return r;
}