UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2018 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include "tar.h" 30 31 #include <string.h> 32 #include <cx/string.h> 33 #include <libidav/utils.h> 34 35 36 const char* tar_error2str(TarError error) { 37 switch(error) { 38 default: break; 39 case TAR_OK: return "ok"; 40 case TAR_PATH_TOO_LONG: return "path too long"; 41 case TAR_FILE_TOO_LARGE: return "file too large"; 42 case TAR_CONTENT_TOO_LARGE: return "tar content too large"; 43 case TAR_UNFINISHED_FILE: return "can''t read a tar header at this position"; 44 case TAR_CONTENT_BROKEN: return "tar content broken"; 45 } 46 return "error"; 47 } 48 49 TarOutputStream* tar_open(FILE *f) { 50 TarOutputStream *tar = malloc(sizeof(TarOutputStream)); 51 tar->file= f; 52 tar->cur_filesize = 0; 53 tar->cur_written = 0; 54 tar->error = 0; 55 return tar; 56 } 57 58 59 static int add_header(TarOutputStream *tar, char *path, uint32_t mode, uint64_t size, time_t mtime, int type) { 60 // split path in prefix and name and check length 61 char *p = util_parent_path(path); 62 const char *n = util_resource_name(path); 63 if(!p || !n) { 64 return -1; 65 } 66 67 cxstring prefix = cx_str(p); 68 cxstring name = cx_str(n); 69 70 if(prefix.ptr[prefix.length-1] == '/') { 71 prefix.length--; 72 } 73 74 if(prefix.length > 154) { 75 tar->error = TAR_PATH_TOO_LONG; 76 free(p); 77 return -1; 78 } 79 if(name.length > 99) { 80 tar->error = TAR_PATH_TOO_LONG; 81 free(p); 82 return -1; 83 } 84 85 // check file length 86 if(size >= 077777777777 ) { 87 tar->error = TAR_FILE_TOO_LARGE; 88 free(p); 89 return -1; 90 } 91 92 // set header fields 93 TarHeader h; 94 memset(&h, 0, sizeof(TarHeader)); 95 96 // name 97 memcpy(h.name, name.ptr, name.length); 98 // mode 99 snprintf(h.mode, 8, "%07o", mode); 100 h.mode[7] = ' '; 101 // uid/gid 102 memset(h.uid, '0', 8); 103 h.uid[7] = ' '; 104 memset(h.gid, '0', 8); 105 h.gid[7] = ' '; 106 // size 107 snprintf(h.size, 12, "%011lo", (unsigned long)size); 108 h.size[11] = ' '; 109 // mtime 110 uint64_t t = (uint64_t)mtime; 111 snprintf(h.mtime, 12, "%011lo", mtime); 112 h.mtime[11] = ' '; 113 // chksum 114 memset(h.chksum, ' ', 8); 115 // typeflag 116 h.typeflag = type; 117 // linkname - zeros 118 // magic 119 snprintf(h.magic, 6, "ustar"); 120 // version 121 h.version[0] = '0'; 122 h.version[1] = '0'; 123 // uname/gname - zero 124 // devmajor/devminor 125 char *devbuf = (char*)h.devmajor; 126 snprintf(devbuf, 16, "%015o", 0); 127 h.devmajor[7] = ' '; 128 h.devminor[7] = ' '; 129 // prefix 130 memcpy(h.prefix, prefix.ptr, prefix.length); 131 132 // compute checksum 133 uint8_t *header = (uint8_t*)&h; 134 uint32_t chksum = 0; 135 for(int i=0;i<512;i++) { 136 chksum += header[i]; 137 } 138 snprintf(h.chksum, 8, "%07o", chksum); 139 140 fwrite(&h, 1, 512, tar->file); 141 142 free(p); 143 144 return 0; 145 } 146 147 148 int tar_add_dir(TarOutputStream *tar, char *path, uint32_t mode, time_t mtime) { 149 return add_header(tar, path, mode, 0, mtime, TAR_TYPE_DIRECTORY); 150 } 151 152 int tar_begin_file( 153 TarOutputStream *tar, 154 char *path, 155 uint32_t mode, 156 uint64_t size, 157 time_t mtime) 158 { 159 if(add_header(tar, path, mode, size, mtime, TAR_TYPE_FILE)) { 160 return -1; 161 } 162 163 tar->cur_filesize = size; 164 tar->cur_written = 0; 165 166 return 0; 167 } 168 169 size_t tar_fwrite(const void *ptr, size_t s, size_t n, TarOutputStream *stream) { 170 size_t w = fwrite(ptr, s, n, stream->file); 171 stream->cur_written += w; 172 if(stream->cur_written > stream->cur_filesize) { 173 stream->error = TAR_CONTENT_TOO_LARGE; 174 return 0; 175 } 176 return w; 177 } 178 179 int tar_end_file(TarOutputStream *tar) { 180 size_t pad = 512 - tar->cur_written % 512; 181 char buf[512]; 182 memset(buf, 0, 512); 183 fwrite(buf, 1, pad, tar->file); 184 185 tar->cur_filesize = 0; 186 tar->cur_written = 0; 187 return 0; 188 } 189 190 int tar_close(TarOutputStream *tar) { 191 char buf[512]; 192 memset(buf, 0, 512); 193 fwrite(buf, 1, 512, tar->file); 194 return 0; 195 } 196 197 TarInputStream* tar_inputstream_open(FILE *f) { 198 TarInputStream *tar = malloc(sizeof(TarInputStream)); 199 memset(tar, 0, sizeof(TarInputStream)); 200 tar->file = f; 201 return tar; 202 } 203 204 TarEntry* tar_read_entry(TarInputStream *tar) { 205 if(tar->cur_read < tar->cur_entry.size) { 206 tar->error = TAR_UNFINISHED_FILE; 207 return NULL; 208 } 209 210 TarHeader h; 211 memset(&h, 0, sizeof(TarHeader)); 212 213 if(fread(&h, 1, 512, tar->file) != 512) { 214 tar->error = TAR_ERROR; 215 return NULL; 216 } 217 218 // some checks 219 tar->error = TAR_CONTENT_BROKEN; // set error for all following returns 220 221 char *buf = (char*)&h; 222 uint64_t chksum = 0; 223 int chksumfield = 8 * 32; 224 for(int i=0;i<148;i++) { 225 chksum += buf[i]; 226 } 227 chksum += chksumfield; 228 for(int i=156;i<512;i++) { 229 chksum += buf[i]; 230 } 231 232 if(chksum != chksumfield) { 233 long long int cks = strtoll(h.chksum, NULL, 8); 234 if(cks != chksum) { 235 return NULL; 236 } 237 } 238 239 if(memcmp(h.magic, "ustar\0", 6)) { 240 if(!memcmp(h.magic, "\0\0\0\0", 4)) { 241 tar->error = TAR_OK; 242 } 243 return NULL; 244 } 245 if(memcmp(h.version, "00", 2)) { 246 return NULL; 247 } 248 249 // check if name and prefix are null terminated 250 int nameterm = 0; 251 int prefixterm = 0; 252 for(int i=0;i<100;i++) { 253 if(h.name[i] == 0) { 254 nameterm = 1; 255 break; 256 } 257 } 258 for(int i=0;i<155;i++) { 259 if(h.prefix[i] == 0) { 260 prefixterm = 1; 261 break; 262 } 263 } 264 if(!nameterm || !prefixterm) { 265 return NULL; 266 } 267 268 // get size 269 if(!(h.size[11] == ' ' || h.size[11] == 0)) { 270 return NULL; 271 } 272 long long int size = strtoll(h.size, NULL, 8); 273 if(size < 0) { 274 return NULL; 275 } 276 277 if(h.name[0] == 0) { 278 return NULL; 279 } 280 281 // get path 282 char *path = h.prefix[0] != 0 ? util_concat_path(h.prefix, h.name) : strdup(h.name); 283 284 if(tar->cur_entry.path) { 285 free(tar->cur_entry.path); 286 } 287 288 tar->cur_entry.path = path; 289 tar->cur_entry.size = (uint64_t)size; 290 tar->cur_entry.type = h.typeflag; 291 tar->cur_read = 0; 292 293 tar->error = TAR_OK; 294 return &tar->cur_entry; 295 } 296 297 size_t tar_fread(void *ptr, size_t s, size_t n, TarInputStream *stream) { 298 size_t bufsize = s*n; 299 size_t available = stream->cur_entry.size - stream->cur_read; 300 301 size_t nb = available > bufsize ? bufsize : available; 302 if(nb == 0) { 303 return 0; 304 } 305 size_t r = fread(ptr, 1, nb, stream->file); 306 if(r != nb) { 307 stream->error = TAR_ERROR; 308 return 0; 309 } 310 311 stream->cur_read += r; 312 if(stream->cur_read >= stream->cur_entry.size) { 313 // read padding 314 size_t pad = 512 - stream->cur_read % 512; 315 char buf[512]; 316 if(fread(buf, 1, pad, stream->file) != pad) { 317 stream->error = TAR_ERROR; 318 return 0; 319 } 320 } 321 322 return r; 323 } 324 325 int tar_seek(TarInputStream *stream, long offset, int whence) { 326 return fseek(stream->file, offset, whence); 327 } 328 329 int tar_inputstream_close(TarInputStream *tar) { 330 if(tar->cur_entry.path) { 331 free(tar->cur_entry.path); 332 } 333 free(tar); 334 return 0; 335 } 336