17 |
#include <sys/types.h> |
#include <sys/types.h> |
18 |
#include <time.h> |
#include <time.h> |
19 |
#include <unistd.h> |
#include <unistd.h> |
20 |
|
#include <utime.h> |
21 |
|
|
22 |
#if defined(__linux__) |
#if defined(__linux__) |
23 |
# include <linux/limits.h> |
# include <linux/limits.h> |
67 |
struct uar_file **files; |
struct uar_file **files; |
68 |
struct uar_file *root; |
struct uar_file *root; |
69 |
enum uar_error ecode; |
enum uar_error ecode; |
|
bool is_stream; |
|
|
uint8_t *buffer; /* Deprecated */ |
|
70 |
FILE *stream; |
FILE *stream; |
71 |
uint64_t stream_size; |
uint64_t stream_size; |
72 |
int last_errno; |
int last_errno; |
73 |
char *err_file; |
char *err_file; |
74 |
|
uint64_t data_start; |
75 |
uar_create_callback_t create_callback; |
uar_create_callback_t create_callback; |
76 |
|
uar_extract_callback_t extract_callback; |
77 |
}; |
}; |
78 |
|
|
79 |
void |
void |
83 |
uar->create_callback = callback; |
uar->create_callback = callback; |
84 |
} |
} |
85 |
|
|
86 |
|
void |
87 |
|
uar_set_extract_callback (struct uar_archive *uar, |
88 |
|
uar_extract_callback_t callback) |
89 |
|
{ |
90 |
|
uar->extract_callback = callback; |
91 |
|
} |
92 |
|
|
93 |
static void |
static void |
94 |
uar_set_error (struct uar_archive *uar, enum uar_error ecode, |
uar_set_error (struct uar_archive *uar, enum uar_error ecode, |
95 |
const char *err_file) |
const char *err_file) |
164 |
if (uar == NULL) |
if (uar == NULL) |
165 |
return NULL; |
return NULL; |
166 |
|
|
|
uar->is_stream = false; |
|
|
uar->buffer = NULL; |
|
167 |
uar->ecode = UAR_SUCCESS; |
uar->ecode = UAR_SUCCESS; |
168 |
uar->header.size = 0; |
uar->header.size = 0; |
169 |
memcpy (uar->header.magic, UAR_MAGIC, 4); |
memcpy (uar->header.magic, UAR_MAGIC, 4); |
220 |
bool |
bool |
221 |
uar_stream_write (struct uar_archive *uar, const char *filename) |
uar_stream_write (struct uar_archive *uar, const char *filename) |
222 |
{ |
{ |
223 |
if (uar == NULL || !uar->is_stream || uar->stream == NULL) |
if (uar == NULL || uar->stream == NULL) |
224 |
return false; |
return false; |
225 |
|
|
226 |
FILE *stream = fopen (filename, "wb"); |
FILE *stream = fopen (filename, "wb"); |
328 |
if (uar == NULL) |
if (uar == NULL) |
329 |
return NULL; |
return NULL; |
330 |
|
|
|
uar->is_stream = true; |
|
331 |
uar->stream = tmpfile (); |
uar->stream = tmpfile (); |
332 |
|
|
333 |
if (uar->stream == NULL) |
if (uar->stream == NULL) |
352 |
const char *fs_filename, struct stat *stinfo) |
const char *fs_filename, struct stat *stinfo) |
353 |
{ |
{ |
354 |
assert (uar != NULL && "uar is NULL"); |
assert (uar != NULL && "uar is NULL"); |
|
assert (uar->is_stream && "uar is not in stream mode"); |
|
355 |
assert (uar_filename != NULL && "uar_filename is NULL"); |
assert (uar_filename != NULL && "uar_filename is NULL"); |
356 |
assert (fs_filename != NULL && "fs_filename is NULL"); |
assert (fs_filename != NULL && "fs_filename is NULL"); |
357 |
|
|
695 |
const char *fs_name, struct stat *stinfo) |
const char *fs_name, struct stat *stinfo) |
696 |
{ |
{ |
697 |
assert (uar != NULL && "uar is NULL"); |
assert (uar != NULL && "uar is NULL"); |
|
assert (uar->is_stream && "uar is not in stream mode"); |
|
698 |
assert (uar_name != NULL && "uar_name is NULL"); |
assert (uar_name != NULL && "uar_name is NULL"); |
699 |
assert (fs_name != NULL && "fs_name is NULL"); |
assert (fs_name != NULL && "fs_name is NULL"); |
700 |
|
|
801 |
if (uar == NULL) |
if (uar == NULL) |
802 |
return NULL; |
return NULL; |
803 |
|
|
|
uar->is_stream = true; |
|
804 |
uar->stream = stream; |
uar->stream = stream; |
805 |
|
|
806 |
fseek (stream, 0, SEEK_END); |
fseek (stream, 0, SEEK_END); |
840 |
return uar; |
return uar; |
841 |
} |
} |
842 |
|
|
843 |
uint64_t read_size = sizeof (struct uar_header) |
uint64_t file_block_size |
844 |
+ (uar->header.nfiles * sizeof (struct uar_file)); |
= sizeof (struct uar_header) |
845 |
|
+ (uar->header.nfiles * sizeof (struct uar_file)); |
846 |
|
uint64_t data_block_start = file_block_size; |
847 |
|
|
848 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
849 |
{ |
{ |
869 |
*/ |
*/ |
870 |
|
|
871 |
if (file->namelen > PATH_MAX || file->namelen == 0 |
if (file->namelen > PATH_MAX || file->namelen == 0 |
872 |
|| read_size + file->namelen > ((uint64_t) size)) |
|| file_block_size + file->namelen > ((uint64_t) size)) |
873 |
{ |
{ |
874 |
/* At a later stage, we might want to rather call a callback |
/* At a later stage, we might want to rather call a callback |
875 |
function instead. */ |
function instead. */ |
878 |
return uar; |
return uar; |
879 |
} |
} |
880 |
|
|
881 |
|
data_block_start += file->namelen; |
882 |
file->name = malloc (file->namelen + 1); |
file->name = malloc (file->namelen + 1); |
883 |
|
|
884 |
if (file->name == NULL) |
if (file->name == NULL) |
904 |
if (file->type == UF_LINK) |
if (file->type == UF_LINK) |
905 |
{ |
{ |
906 |
if (file->data.link.loclen > PATH_MAX |
if (file->data.link.loclen > PATH_MAX |
907 |
|| read_size + file->data.link.loclen |
|| file_block_size + file->data.link.loclen |
908 |
> ((uint64_t) size)) |
> ((uint64_t) size)) |
909 |
{ |
{ |
910 |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
uar_set_error (uar, UAR_INVALID_ARCHIVE, NULL); |
913 |
return uar; |
return uar; |
914 |
} |
} |
915 |
|
|
916 |
|
data_block_start += file->data.link.loclen; |
917 |
file->data.link.loc = malloc (file->data.link.loclen + 1); |
file->data.link.loc = malloc (file->data.link.loclen + 1); |
918 |
|
|
919 |
if (file->data.link.loc == NULL) |
if (file->data.link.loc == NULL) |
941 |
uar->files[i] = file; |
uar->files[i] = file; |
942 |
} |
} |
943 |
|
|
944 |
|
uar->data_start = data_block_start; |
945 |
return uar; |
return uar; |
946 |
} |
} |
947 |
|
|
948 |
struct uar_archive * |
static bool |
949 |
uar_open (const char *filename) |
uar_stream_extract_file (struct uar_archive *uar, struct uar_file *file, |
950 |
|
const char *path) |
951 |
{ |
{ |
952 |
struct uar_archive *uar = NULL; |
FILE *stream = fopen (path, "wb"); |
953 |
FILE *stream = NULL; |
uint8_t buffer[1024]; |
954 |
int cerrno; |
uint64_t size = file->data.size; |
955 |
|
|
956 |
errno = 0; |
if (stream == NULL) |
957 |
uar = uar_create (); |
{ |
958 |
|
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
959 |
|
perror ("fopen"); |
960 |
|
|
961 |
if (uar == NULL) |
if (uar->extract_callback != NULL) |
962 |
return NULL; |
uar->extract_callback (uar, file, file->name, path, |
963 |
|
UAR_ELEVEL_WARNING, strerror (errno)); |
964 |
|
|
965 |
stream = fopen (filename, "rb"); |
return false; |
966 |
|
} |
967 |
|
|
968 |
if (stream == NULL) |
if (fseek (uar->stream, uar->data_start + file->offset, SEEK_SET) != 0) |
969 |
{ |
{ |
970 |
uar_set_error (uar, UAR_IO_ERROR, NULL); |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
|
goto uar_open_ret; |
|
|
} |
|
971 |
|
|
972 |
fseek (stream, 0, SEEK_END); |
if (uar->extract_callback != NULL) |
973 |
size_t size = ftell (stream); |
uar->extract_callback (uar, file, file->name, path, |
974 |
fseek (stream, 0, SEEK_SET); |
UAR_ELEVEL_WARNING, strerror (errno)); |
975 |
|
|
976 |
if (fread (&uar->header, sizeof (struct uar_header), 1, stream) != 1) |
fclose (stream); |
977 |
{ |
return false; |
|
uar_set_error (uar, UAR_IO_ERROR, NULL); |
|
|
goto uar_open_ret; |
|
978 |
} |
} |
979 |
|
|
980 |
if (memcmp (uar->header.magic, UAR_MAGIC, 4) != 0) |
while (size > 0) |
981 |
{ |
{ |
982 |
uar_set_error (uar, UAR_INVALID_MAGIC, NULL); |
size_t read_size = size > sizeof (buffer) ? sizeof (buffer) : size; |
|
goto uar_open_ret; |
|
|
} |
|
983 |
|
|
984 |
uint64_t filearr_size = uar->header.nfiles * sizeof (struct uar_file); |
if (fread (buffer, 1, read_size, uar->stream) != read_size) |
985 |
|
{ |
986 |
|
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
987 |
|
|
988 |
if (filearr_size > size) |
if (uar->extract_callback != NULL) |
989 |
{ |
uar->extract_callback (uar, file, file->name, path, |
990 |
uar_set_error (uar, UAR_IO_ERROR, NULL); |
UAR_ELEVEL_WARNING, |
991 |
goto uar_open_ret; |
strerror (errno)); |
992 |
|
|
993 |
|
fclose (stream); |
994 |
|
return false; |
995 |
|
} |
996 |
|
|
997 |
|
if (fwrite (buffer, 1, read_size, stream) != read_size) |
998 |
|
{ |
999 |
|
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1000 |
|
|
1001 |
|
if (uar->extract_callback != NULL) |
1002 |
|
uar->extract_callback (uar, file, file->name, path, |
1003 |
|
UAR_ELEVEL_WARNING, |
1004 |
|
strerror (errno)); |
1005 |
|
|
1006 |
|
fclose (stream); |
1007 |
|
return false; |
1008 |
|
} |
1009 |
|
|
1010 |
|
size -= read_size; |
1011 |
} |
} |
1012 |
|
|
1013 |
if (uar->header.size > size) |
if (fchmod (fileno (stream), file->mode) != 0) |
1014 |
{ |
{ |
1015 |
uar_set_error (uar, UAR_IO_ERROR, NULL); |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
|
goto uar_open_ret; |
|
|
} |
|
1016 |
|
|
1017 |
uar->files = calloc (uar->header.nfiles, sizeof (struct uar_file *)); |
if (uar->extract_callback != NULL) |
1018 |
|
uar->extract_callback (uar, file, file->name, path, |
1019 |
|
UAR_ELEVEL_WARNING, strerror (errno)); |
1020 |
|
|
1021 |
if (uar->files == NULL) |
fclose (stream); |
1022 |
{ |
return false; |
|
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
|
|
goto uar_open_ret; |
|
1023 |
} |
} |
1024 |
|
|
1025 |
|
fclose (stream); |
1026 |
|
return true; |
1027 |
|
} |
1028 |
|
|
1029 |
|
bool |
1030 |
|
uar_stream_extract (struct uar_archive *uar, const char *dest) |
1031 |
|
{ |
1032 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1033 |
{ |
{ |
1034 |
struct uar_file *file = malloc (sizeof (struct uar_file)); |
struct uar_file *file = uar->files[i]; |
1035 |
|
char *name = file->name; |
1036 |
|
size_t diff = 0; |
1037 |
|
bool is_root = strcmp (file->name, "/") == 0; |
1038 |
|
|
1039 |
if (file == NULL) |
if (!is_root) |
1040 |
{ |
{ |
1041 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
if (name[0] == '/') |
1042 |
goto uar_open_ret; |
diff += 1; |
1043 |
|
|
1044 |
|
if (strncmp (name, "./", 2) == 0) |
1045 |
|
diff += 2; |
1046 |
|
else if (strncmp (name, "../", 3) == 0) |
1047 |
|
diff += 3; |
1048 |
|
else if (strcmp (name, "..") == 0) |
1049 |
|
diff += 2; |
1050 |
|
else if (strcmp (name, ".") == 0) |
1051 |
|
diff += 1; |
1052 |
} |
} |
1053 |
|
|
1054 |
if (fread (file, sizeof (struct uar_file), 1, stream) != 1) |
char *path |
1055 |
|
= is_root ? (char *) dest |
1056 |
|
: path_concat (dest, file->name + diff, strlen (dest), |
1057 |
|
file->namelen - diff); |
1058 |
|
|
1059 |
|
if (path == NULL) |
1060 |
{ |
{ |
1061 |
uar_set_error (uar, UAR_IO_ERROR, NULL); |
uar_set_error (uar, UAR_OUT_OF_MEMORY, file->name); |
1062 |
goto uar_open_ret; |
return false; |
1063 |
} |
} |
1064 |
|
|
1065 |
if (file->namelen > PATH_MAX) |
if (!is_root) |
1066 |
{ |
{ |
1067 |
uar_set_error (uar, UAR_INVALID_PATH, NULL); |
switch (file->type) |
1068 |
goto uar_open_ret; |
{ |
1069 |
|
case UF_FILE: |
1070 |
|
if (!uar_stream_extract_file (uar, file, path)) |
1071 |
|
{ |
1072 |
|
free (path); |
1073 |
|
return false; |
1074 |
|
} |
1075 |
|
break; |
1076 |
|
case UF_DIR: |
1077 |
|
if (mkdir (path, file->mode) != 0) |
1078 |
|
{ |
1079 |
|
uar_set_error (uar, UAR_SYSCALL_ERROR, |
1080 |
|
path); |
1081 |
|
|
1082 |
|
if (uar->extract_callback != NULL) |
1083 |
|
uar->extract_callback ( |
1084 |
|
uar, file, file->name, path, |
1085 |
|
UAR_ELEVEL_WARNING, |
1086 |
|
strerror (errno)); |
1087 |
|
|
1088 |
|
free (path); |
1089 |
|
return false; |
1090 |
|
} |
1091 |
|
break; |
1092 |
|
case UF_LINK: |
1093 |
|
if (symlink (file->data.link.loc, path) != 0) |
1094 |
|
{ |
1095 |
|
uar_set_error (uar, UAR_SYSCALL_ERROR, |
1096 |
|
path); |
1097 |
|
|
1098 |
|
if (uar->extract_callback != NULL) |
1099 |
|
uar->extract_callback ( |
1100 |
|
uar, file, file->name, path, |
1101 |
|
UAR_ELEVEL_WARNING, |
1102 |
|
strerror (errno)); |
1103 |
|
|
1104 |
|
free (path); |
1105 |
|
return false; |
1106 |
|
} |
1107 |
|
break; |
1108 |
|
default: |
1109 |
|
uar_set_error (uar, UAR_INVALID_FILE, file->name); |
1110 |
|
|
1111 |
|
if (uar->extract_callback != NULL) |
1112 |
|
uar->extract_callback (uar, file, file->name, |
1113 |
|
path, UAR_ELEVEL_WARNING, |
1114 |
|
strerror (errno)); |
1115 |
|
|
1116 |
|
free (path); |
1117 |
|
return false; |
1118 |
|
} |
1119 |
} |
} |
1120 |
|
|
1121 |
file->name = malloc (file->namelen + 1); |
struct utimbuf times |
1122 |
|
= { .actime = time (NULL), .modtime = file->mtime }; |
1123 |
|
|
1124 |
if (file->name == NULL) |
if (utime (path, ×) != 0) |
1125 |
{ |
{ |
1126 |
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
1127 |
goto uar_open_ret; |
|
1128 |
|
if (uar->extract_callback != NULL) |
1129 |
|
uar->extract_callback (uar, file, file->name, path, |
1130 |
|
UAR_ELEVEL_WARNING, |
1131 |
|
strerror (errno)); |
1132 |
|
|
1133 |
|
if (!is_root) |
1134 |
|
free (path); |
1135 |
|
|
1136 |
|
return false; |
1137 |
} |
} |
1138 |
|
|
1139 |
if (fread (file->name, 1, file->namelen, stream) != file->namelen) |
if (chown (path, file->uid, file->gid) != 0) |
1140 |
{ |
{ |
1141 |
uar_set_error (uar, UAR_IO_ERROR, file->name); |
uar_set_error (uar, UAR_SYSCALL_ERROR, path); |
|
goto uar_open_ret; |
|
|
} |
|
1142 |
|
|
1143 |
file->name[file->namelen] = 0; |
if (uar->extract_callback != NULL) |
1144 |
uar->files[i] = file; |
uar->extract_callback (uar, file, file->name, path, |
1145 |
} |
UAR_ELEVEL_WARNING, |
1146 |
|
strerror (errno)); |
1147 |
|
|
1148 |
uar->buffer = malloc (uar->header.size + 1); |
if (!is_root) |
1149 |
|
free (path); |
1150 |
|
|
1151 |
if (uar->buffer == NULL) |
return false; |
1152 |
{ |
} |
|
uar_set_error (uar, UAR_OUT_OF_MEMORY, NULL); |
|
|
goto uar_open_ret; |
|
|
} |
|
1153 |
|
|
1154 |
if (fread (uar->buffer, 1, uar->header.size, stream) != uar->header.size) |
if (uar->extract_callback != NULL) |
1155 |
{ |
uar->extract_callback (uar, file, file->name, path, |
1156 |
uar_set_error (uar, UAR_IO_ERROR, NULL); |
UAR_ELEVEL_NONE, NULL); |
1157 |
goto uar_open_ret; |
|
1158 |
|
if (!is_root) |
1159 |
|
free (path); |
1160 |
} |
} |
1161 |
|
|
1162 |
uar_open_ret: |
return true; |
|
cerrno = errno; |
|
|
fclose (stream); |
|
|
errno = cerrno; |
|
|
return uar; |
|
1163 |
} |
} |
1164 |
|
|
1165 |
void |
void |
1181 |
if (uar == NULL) |
if (uar == NULL) |
1182 |
return; |
return; |
1183 |
|
|
1184 |
if (uar->is_stream) |
fclose (uar->stream); |
|
fclose (uar->stream); |
|
|
else |
|
|
free (uar->buffer); |
|
1185 |
|
|
1186 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1187 |
{ |
{ |
1272 |
return file; |
return file; |
1273 |
} |
} |
1274 |
|
|
1275 |
struct uar_file * |
void |
1276 |
uar_add_file (struct uar_archive *restrict uar, const char *name, |
uar_file_set_mode (struct uar_file *file, mode_t mode) |
|
const char *path, struct stat *stinfo) |
|
1277 |
{ |
{ |
1278 |
assert (uar != NULL && "uar is NULL"); |
file->mode = mode; |
|
assert (name != NULL && "name is NULL"); |
|
|
assert (path != NULL && "path is NULL"); |
|
|
assert (!uar->is_stream && "uar in non-stream mode is not supported yet"); |
|
|
|
|
|
uint64_t namelen = strlen (name); |
|
|
struct stat *file_stinfo = stinfo, st_stinfo = { 0 }; |
|
|
|
|
|
if (namelen >= PATH_MAX) |
|
|
{ |
|
|
uar_set_error (uar, UAR_INVALID_PATH, path); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
if (file_stinfo == NULL) |
|
|
{ |
|
|
if (lstat (path, &st_stinfo) != 0) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, path); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
file_stinfo = &st_stinfo; |
|
|
} |
|
|
|
|
|
FILE *stream = fopen (path, "rb"); |
|
|
|
|
|
if (stream == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, path); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
fseek (stream, 0, SEEK_END); |
|
|
long size = ftell (stream); |
|
|
|
|
|
if (size < 0) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, path); |
|
|
fclose (stream); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
fseek (stream, 0, SEEK_SET); |
|
|
|
|
|
struct uar_file *file |
|
|
= uar_file_create (name, namelen, size, uar->header.size); |
|
|
|
|
|
if (file == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_OUT_OF_MEMORY, path); |
|
|
fclose (stream); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
file->mtime = file_stinfo->st_mtime; |
|
|
uar->header.size += size; |
|
|
|
|
|
if (!uar_add_file_entry (uar, file)) |
|
|
{ |
|
|
uar_file_destroy (file); |
|
|
fclose (stream); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
uar->buffer = realloc (uar->buffer, uar->header.size); |
|
|
|
|
|
if (uar->buffer == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_OUT_OF_MEMORY, path); |
|
|
fclose (stream); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
if (size != 0 && fread (uar->buffer + file->offset, size, 1, stream) != 1) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, path); |
|
|
fclose (stream); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
fclose (stream); |
|
|
return file; |
|
1279 |
} |
} |
1280 |
|
|
1281 |
struct uar_file * |
static void |
1282 |
uar_add_dir (struct uar_archive *uar, const char *dname, const char *path, |
uar_debug_print_file_contents (const struct uar_archive *uar, |
1283 |
bool (*callback) (struct uar_file *file, const char *fullname, |
struct uar_file *file) |
|
const char *fullpath)) |
|
1284 |
{ |
{ |
1285 |
assert (uar != NULL && "uar is NULL"); |
printf (" contents:\n"); |
1286 |
assert (dname != NULL && "dname is NULL"); |
printf ("==================\n"); |
1287 |
assert (path != NULL && "path is NULL"); |
fflush (stdout); |
|
assert (!uar->is_stream && "uar in non-stream mode is not supported yet"); |
|
|
|
|
|
char *name = (char *) dname; |
|
|
bool free_name = false; |
|
|
int cerrno; |
|
|
uint64_t namelen; |
|
|
|
|
|
if (strcmp (name, ".") == 0) |
|
|
{ |
|
|
name = strdup ("/"); |
|
|
free_name = true; |
|
|
namelen = 1; |
|
|
} |
|
|
else |
|
|
namelen = strlen (name); |
|
|
|
|
|
if (namelen >= PATH_MAX) |
|
|
{ |
|
|
uar_set_error (uar, UAR_INVALID_PATH, path); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
DIR *dir = opendir (path); |
|
|
struct dirent *entry = NULL; |
|
|
|
|
|
if (dir == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_INVALID_FILE, path); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
struct uar_file *dir_file |
|
|
= uar_file_create (name, namelen, 0, uar->header.size); |
|
|
uint64_t dir_size = 0; |
|
|
|
|
|
dir_file->type = UF_DIR; |
|
1288 |
|
|
1289 |
if (callback != NULL && !callback (dir_file, name, path)) |
if (fseek (uar->stream, uar->data_start + file->offset, SEEK_SET) != 0) |
1290 |
{ |
{ |
1291 |
uar_set_error (uar, UAR_SUCCESS, NULL); |
perror ("fseek"); |
1292 |
uar_file_destroy (dir_file); |
return; |
|
return NULL; |
|
1293 |
} |
} |
1294 |
|
|
1295 |
if (!uar_add_file_entry (uar, dir_file)) |
uint8_t buffer[1024]; |
|
{ |
|
|
uar_file_destroy (dir_file); |
|
|
return NULL; |
|
|
} |
|
1296 |
|
|
1297 |
while ((entry = readdir (dir)) != NULL) |
while (file->data.size > 0) |
1298 |
{ |
{ |
1299 |
if (strcmp (entry->d_name, ".") == 0 |
size_t size = file->data.size > sizeof (buffer) ? sizeof (buffer) |
1300 |
|| strcmp (entry->d_name, "..") == 0) |
: file->data.size; |
|
continue; |
|
|
|
|
|
struct stat stinfo = { 0 }; |
|
|
|
|
|
if (256 + namelen >= PATH_MAX) |
|
|
{ |
|
|
uar_set_error (uar, UAR_INVALID_PATH, path); |
|
|
uar_file_destroy (dir_file); |
|
|
closedir (dir); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
uint64_t dnamelen = strlen (entry->d_name); |
|
|
|
|
|
char *fullpath |
|
|
= path_concat (path, entry->d_name, strlen (path), dnamelen); |
|
|
assert (fullpath != NULL); |
|
|
|
|
|
char *fullname |
|
|
= path_concat (name, entry->d_name, namelen, dnamelen); |
|
|
assert (fullname != NULL); |
|
1301 |
|
|
1302 |
if (lstat (fullpath, &stinfo) != 0) |
if (fread (buffer, 1, size, uar->stream) != size) |
1303 |
{ |
{ |
1304 |
uar_set_error (uar, UAR_IO_ERROR, fullpath); |
perror ("read"); |
1305 |
goto uar_add_dir_error; |
return; |
1306 |
} |
} |
1307 |
|
|
1308 |
if (S_ISREG (stinfo.st_mode)) |
for (size_t i = 0; i < size; i++) |
|
{ |
|
|
struct uar_file *file |
|
|
= uar_add_file (uar, fullname, fullpath, &stinfo); |
|
|
|
|
|
if (file == NULL) |
|
|
{ |
|
|
goto uar_add_dir_error; |
|
|
} |
|
|
|
|
|
if (callback != NULL |
|
|
&& !callback (file, fullname, fullpath)) |
|
|
{ |
|
|
uar_set_error (uar, UAR_SUCCESS, NULL); |
|
|
goto uar_add_dir_error; |
|
|
} |
|
|
|
|
|
file->mode = stinfo.st_mode; |
|
|
dir_size += file->data.size; |
|
|
} |
|
|
else if (S_ISDIR (stinfo.st_mode)) |
|
1309 |
{ |
{ |
1310 |
struct uar_file *direntry |
if (buffer[i] == '\n') |
1311 |
= uar_add_dir (uar, fullname, fullpath, callback); |
putchar ('\n'); |
1312 |
|
else |
1313 |
if (direntry == NULL) |
putchar (buffer[i]); |
|
{ |
|
|
goto uar_add_dir_error; |
|
|
} |
|
|
|
|
|
direntry->mode = stinfo.st_mode; |
|
|
dir_size += direntry->data.size; |
|
1314 |
} |
} |
|
else |
|
|
assert (false && "Not supported"); |
|
|
|
|
|
free (fullpath); |
|
|
free (fullname); |
|
1315 |
|
|
1316 |
continue; |
file->data.size -= size; |
|
|
|
|
uar_add_dir_error: |
|
|
cerrno = errno; |
|
|
uar_file_destroy (dir_file); |
|
|
free (fullpath); |
|
|
free (fullname); |
|
|
errno = cerrno; |
|
|
goto uar_add_dir_end; |
|
|
} |
|
|
|
|
|
dir_file->data.size = dir_size; |
|
|
|
|
|
uar_add_dir_end: |
|
|
cerrno = errno; |
|
|
closedir (dir); |
|
|
|
|
|
if (free_name) |
|
|
free (name); |
|
|
|
|
|
errno = cerrno; |
|
|
return dir_file; |
|
|
} |
|
|
|
|
|
void |
|
|
uar_file_set_mode (struct uar_file *file, mode_t mode) |
|
|
{ |
|
|
file->mode = mode; |
|
|
} |
|
|
|
|
|
static void |
|
|
uar_debug_print_file_contents (const struct uar_archive *uar, |
|
|
struct uar_file *file) |
|
|
{ |
|
|
printf (" contents:\n"); |
|
|
printf ("==================\n"); |
|
|
fflush (stdout); |
|
|
|
|
|
ssize_t size |
|
|
= write (STDOUT_FILENO, uar->buffer + file->offset, file->data.size); |
|
|
|
|
|
if (size == -1 || ((uint64_t) size) != file->data.size) |
|
|
{ |
|
|
perror ("write"); |
|
|
return; |
|
1317 |
} |
} |
1318 |
|
|
1319 |
putchar ('\n'); |
putchar ('\n'); |
1330 |
printf (" flags: %u\n", uar->header.flags); |
printf (" flags: %u\n", uar->header.flags); |
1331 |
printf (" nfiles: %lu\n", uar->header.nfiles); |
printf (" nfiles: %lu\n", uar->header.nfiles); |
1332 |
printf (" size: %lu\n", uar->header.size); |
printf (" size: %lu\n", uar->header.size); |
|
printf (" stream?: %i\n", uar->is_stream); |
|
1333 |
|
|
1334 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1335 |
{ |
{ |
1359 |
} |
} |
1360 |
|
|
1361 |
bool |
bool |
1362 |
uar_write (struct uar_archive *uar, const char *filename) |
uar_stream_iterate (struct uar_archive *uar, |
1363 |
{ |
bool (*callback) (struct uar_file *file, void *data), |
1364 |
FILE *stream = fopen (filename, "wb"); |
void *data) |
|
|
|
|
if (stream == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, filename); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (fwrite (&uar->header, sizeof (struct uar_header), 1, stream) != 1) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, filename); |
|
|
fclose (stream); |
|
|
return false; |
|
|
} |
|
|
|
|
|
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
|
|
{ |
|
|
struct uar_file *file = uar->files[i]; |
|
|
|
|
|
if (fwrite (file, sizeof (struct uar_file), 1, stream) != 1) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, file->name); |
|
|
fclose (stream); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (fwrite (file->name, 1, file->namelen, stream) != file->namelen) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, file->name); |
|
|
fclose (stream); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
if (fwrite (uar->buffer, 1, uar->header.size, stream) != uar->header.size) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, NULL); |
|
|
fclose (stream); |
|
|
return false; |
|
|
} |
|
|
|
|
|
fclose (stream); |
|
|
return true; |
|
|
} |
|
|
|
|
|
bool |
|
|
uar_extract (struct uar_archive *uar, const char *cwd, |
|
|
bool (*callback) (struct uar_file *file)) |
|
|
{ |
|
|
if (cwd != NULL && chdir (cwd) != 0) |
|
|
{ |
|
|
uar_set_error (uar, UAR_SYSTEM_ERROR, NULL); |
|
|
return false; |
|
|
} |
|
|
|
|
|
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
|
|
{ |
|
|
struct uar_file *file = uar->files[i]; |
|
|
|
|
|
if (callback != NULL && !callback (file)) |
|
|
return false; |
|
|
|
|
|
char *name = file->name; |
|
|
|
|
|
if (name[0] == '/') |
|
|
name += 2; |
|
|
|
|
|
switch (file->type) |
|
|
{ |
|
|
case UF_FILE: |
|
|
{ |
|
|
FILE *stream = fopen (name, "wb"); |
|
|
|
|
|
if (stream == NULL) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, name); |
|
|
return false; |
|
|
} |
|
|
|
|
|
if (fwrite (uar->buffer + file->offset, 1, |
|
|
file->data.size, stream) |
|
|
!= file->data.size) |
|
|
{ |
|
|
uar_set_error (uar, UAR_IO_ERROR, name); |
|
|
return false; |
|
|
} |
|
|
|
|
|
fchmod (fileno (stream), file->mode & 07777); |
|
|
fclose (stream); |
|
|
} |
|
|
break; |
|
|
|
|
|
case UF_DIR: |
|
|
if (file->namelen == 1 && file->name[0] == '/') |
|
|
continue; |
|
|
|
|
|
if (mkdir (name, file->mode) != 0) |
|
|
{ |
|
|
uar_set_error (uar, UAR_SYSTEM_ERROR, name); |
|
|
return false; |
|
|
} |
|
|
|
|
|
break; |
|
|
|
|
|
default: |
|
|
assert (false && "unknown file type"); |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
return true; |
|
|
} |
|
|
|
|
|
bool |
|
|
uar_iterate (struct uar_archive *uar, |
|
|
bool (*callback) (struct uar_file *file, void *data), void *data) |
|
1365 |
{ |
{ |
1366 |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
for (uint64_t i = 0; i < uar->header.nfiles; i++) |
1367 |
{ |
{ |