/
fileio.c
724 lines (595 loc) · 20.2 KB
1
2
3
4
5
6
7
8
/**
* MojoSetup; a portable, flexible installation application.
*
* Please see the file LICENSE.txt in the source's root directory.
*
* This file written by Ryan C. Gordon.
*/
9
#include "fileio.h"
10
#include "platform.h"
11
12
13
14
typedef MojoArchive* (*MojoArchiveCreateEntryPoint)(MojoInput *io);
MojoArchive *MojoArchive_createZIP(MojoInput *io);
15
MojoArchive *MojoArchive_createTAR(MojoInput *io);
16
MojoArchive *MojoArchive_createUZ2(MojoInput *io);
17
18
19
20
21
typedef struct
{
const char *ext;
MojoArchiveCreateEntryPoint create;
22
boolean hasMagic; // can determine file type from contents?
23
24
25
26
} MojoArchiveType;
static const MojoArchiveType archives[] =
{
27
28
29
30
31
32
33
34
{ "zip", MojoArchive_createZIP, true },
{ "tar", MojoArchive_createTAR, true },
{ "tar.gz", MojoArchive_createTAR, true },
{ "tar.bz2", MojoArchive_createTAR, true },
{ "tgz", MojoArchive_createTAR, true },
{ "tbz2", MojoArchive_createTAR, true },
{ "tb2", MojoArchive_createTAR, true },
{ "tbz", MojoArchive_createTAR, true },
35
{ "uz2", MojoArchive_createUZ2, false },
36
37
};
38
MojoArchive *MojoArchive_newFromInput(MojoInput *io, const char *origfname)
39
40
41
{
int i;
MojoArchive *retval = NULL;
42
43
44
45
const char *ext = NULL;
if (origfname != NULL)
{
46
ext = strrchr(origfname, '/');
47
48
49
50
51
52
if (ext == NULL)
ext = strchr(origfname, '.');
else
ext = strchr(ext+1, '.');
} // if
53
while (ext != NULL)
54
{
55
// Try for an exact match by filename extension.
56
ext++; // skip that '.'
57
58
for (i = 0; i < STATICARRAYLEN(archives); i++)
{
59
60
61
const MojoArchiveType *arc = &archives[i];
if (strcasecmp(ext, arc->ext) == 0)
return arc->create(io);
62
} // for
63
64
ext = strchr(ext, '.');
} // while
65
66
// Try any that could be determined without the file extension...
67
68
for (i = 0; i < STATICARRAYLEN(archives); i++)
{
69
70
const MojoArchiveType *arc = &archives[i];
if ((arc->hasMagic) && ((retval = arc->create(io)) != NULL))
71
72
73
74
75
76
77
78
return retval;
} // for
io->close(io);
return NULL; // nothing can handle this data.
} // MojoArchive_newFromInput
79
void MojoArchive_resetEntry(MojoArchiveEntry *info)
80
81
{
free(info->filename);
82
free(info->linkdest);
83
84
memset(info, '\0', sizeof (MojoArchiveEntry));
} // MojoArchive_resetEntry
85
86
87
88
// !!! FIXME: I'd rather not use a callback here, but I can't see a cleaner
// !!! FIXME: way right now...
89
boolean MojoInput_toPhysicalFile(MojoInput *in, const char *fname, uint16 perms,
90
MojoChecksums *checksums, int64 maxbytes,
91
MojoInput_FileCopyCallback cb, void *data)
92
{
93
94
boolean retval = false;
uint32 start = MojoPlatform_ticks();
95
void *out = NULL;
96
boolean iofailure = false;
97
98
int64 flen = 0;
int64 bw = 0;
99
MojoChecksumContext sumctx;
100
101
102
103
if (in == NULL)
return false;
104
105
106
107
108
109
if (checksums != NULL)
{
memset(checksums, '\0', sizeof (MojoChecksums));
MojoChecksum_init(&sumctx);
} // if
110
111
112
113
114
115
// Wait for a ready(), so length() can be meaningful on network streams.
while ((!in->ready(in)) && (!iofailure))
{
MojoPlatform_sleep(100);
if (cb != NULL)
{
116
if (!cb(MojoPlatform_ticks() - start, 0, 0, -1, data))
117
118
119
120
iofailure = true;
} // if
} // while
121
flen = in->length(in);
122
123
if ((maxbytes >= 0) && (flen > maxbytes))
flen = maxbytes;
124
125
MojoPlatform_unlink(fname);
126
if (!iofailure)
127
128
129
130
131
{
const uint32 flags = MOJOFILE_WRITE|MOJOFILE_CREATE|MOJOFILE_TRUNCATE;
const uint16 mode = MojoPlatform_defaultFilePerms();
out = MojoPlatform_open(fname, flags, mode);
} // if
132
133
if (out != NULL)
134
{
135
while (!iofailure)
136
{
137
int64 br = 0;
138
139
140
141
142
143
144
145
146
147
148
149
150
int64 maxread = sizeof (scratchbuf_128k);
// see if we need to clamp to eof or maxbytes...
if (flen >= 0)
{
const int64 avail = flen - bw;
if (avail < maxread)
{
maxread = avail;
if (maxread == 0)
break; // nothing left to do, break out.
} // if
} // if
151
152
153
154
// If there's a callback, then poll. Otherwise, just block on
// the reads from the MojoInput.
if ((cb != NULL) && (!in->ready(in)))
155
156
MojoPlatform_sleep(100);
else
157
{
158
br = in->read(in, scratchbuf_128k, (uint32) maxread);
159
160
161
162
163
if (br == 0) // we're done!
break;
else if (br < 0)
iofailure = true;
else
164
{
165
if (MojoPlatform_write(out, scratchbuf_128k, (uint32) br) != br)
166
iofailure = true;
167
else
168
169
{
if (checksums != NULL)
170
MojoChecksum_append(&sumctx, scratchbuf_128k, (uint32) br);
171
bw += br;
172
} // else
173
} // else
174
} // else
175
176
177
if (cb != NULL)
{
178
if (!cb(MojoPlatform_ticks() - start, br, bw, flen, data))
179
180
181
182
iofailure = true;
} // if
} // while
183
if (MojoPlatform_close(out) != 0)
184
iofailure = true;
185
186
else if (bw != flen)
iofailure = true;
187
188
189
190
191
192
if (iofailure)
MojoPlatform_unlink(fname);
else
{
MojoPlatform_chmod(fname, perms);
193
194
if (checksums != NULL)
MojoChecksum_finish(&sumctx, checksums);
195
196
retval = true;
} // else
197
198
} // if
199
200
in->close(in);
return retval;
201
} // MojoInput_toPhysicalFile
202
203
204
205
206
MojoInput *MojoInput_newFromArchivePath(MojoArchive *ar, const char *fname)
{
MojoInput *retval = NULL;
207
if (ar->enumerate(ar))
208
{
209
const MojoArchiveEntry *entinfo;
210
211
while ((entinfo = ar->enumNext(ar)) != NULL)
{
212
if (strcmp(entinfo->filename, fname) == 0)
213
214
215
216
217
218
219
220
221
222
223
224
{
if (entinfo->type == MOJOARCHIVE_ENTRY_FILE)
retval = ar->openCurrentEntry(ar);
break;
} // if
} // while
} // if
return retval;
} // MojoInput_newFromArchivePath
225
226
227
// MojoInputs from files on the OS filesystem.
228
229
typedef struct
{
230
void *handle;
231
232
233
char *path;
} MojoInputFileInstance;
234
235
236
237
238
239
static boolean MojoInput_file_ready(MojoInput *io)
{
// !!! FIXME: select()? Does that help with network filesystems?
return true;
} // MojoInput_file_ready
240
static int64 MojoInput_file_read(MojoInput *io, void *buf, uint32 bufsize)
241
{
242
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
243
return MojoPlatform_read(inst->handle, buf, bufsize);
244
245
246
247
} // MojoInput_file_read
static boolean MojoInput_file_seek(MojoInput *io, uint64 pos)
{
248
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
249
return (MojoPlatform_seek(inst->handle, pos, MOJOSEEK_SET) == pos);
250
251
} // MojoInput_file_seek
252
static int64 MojoInput_file_tell(MojoInput *io)
253
{
254
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
255
return MojoPlatform_tell(inst->handle);
256
257
} // MojoInput_file_tell
258
259
260
static int64 MojoInput_file_length(MojoInput *io)
{
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
261
return MojoPlatform_flen(inst->handle);
262
263
264
265
266
267
268
269
} // MojoInput_file_length
static MojoInput *MojoInput_file_duplicate(MojoInput *io)
{
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
return MojoInput_newFromFile(inst->path);
} // MojoInput_file_duplicate
270
271
static void MojoInput_file_close(MojoInput *io)
{
272
MojoInputFileInstance *inst = (MojoInputFileInstance *) io->opaque;
273
MojoPlatform_close(inst->handle);
274
275
free(inst->path);
free(inst);
276
277
278
free(io);
} // MojoInput_file_close
279
MojoInput *MojoInput_newFromFile(const char *path)
280
{
281
MojoInput *io = NULL;
282
void *f = NULL;
283
284
f = MojoPlatform_open(path, MOJOFILE_READ, 0);
285
if (f != NULL)
286
{
287
288
289
290
291
292
MojoInputFileInstance *inst;
inst = (MojoInputFileInstance *) xmalloc(sizeof (MojoInputFileInstance));
inst->path = xstrdup(path);
inst->handle = f;
io = (MojoInput *) xmalloc(sizeof (MojoInput));
293
io->ready = MojoInput_file_ready;
294
295
296
297
298
299
300
io->read = MojoInput_file_read;
io->seek = MojoInput_file_seek;
io->tell = MojoInput_file_tell;
io->length = MojoInput_file_length;
io->duplicate = MojoInput_file_duplicate;
io->close = MojoInput_file_close;
io->opaque = inst;
301
} // if
302
303
304
305
306
return io;
} // MojoInput_newFromFile
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
// MojoInputs from blocks of memory.
typedef struct
{
void *ptr; // original pointer from xmalloc()
uint32 *refcount; // address in xmalloc()'d block for reference count.
const uint8 *data; // base of actual "file" data in xmalloc()'d block.
uint32 len; // size, in bytes, of "file" data.
uint32 pos; // current read position.
} MojoInputMemInstance;
static boolean MojoInput_memory_ready(MojoInput *io)
{
return true; // always ready!
} // MojoInput_memory_ready
static int64 MojoInput_memory_read(MojoInput *io, void *buf, uint32 bufsize)
{
MojoInputMemInstance *inst = (MojoInputMemInstance *) io->opaque;
const uint32 avail = inst->len - inst->pos;
if (bufsize > avail)
bufsize = avail;
memcpy(buf, inst->data + inst->pos, bufsize);
inst->pos += bufsize;
return bufsize;
} // MojoInput_memory_read
static boolean MojoInput_memory_seek(MojoInput *io, uint64 pos)
{
MojoInputMemInstance *inst = (MojoInputMemInstance *) io->opaque;
if (pos > (uint64) inst->len)
return false;
inst->pos = (uint32) pos;
return true;
} // MojoInput_memory_seek
static int64 MojoInput_memory_tell(MojoInput *io)
{
MojoInputMemInstance *inst = (MojoInputMemInstance *) io->opaque;
return (int64) inst->pos;
} // MojoInput_memory_tell
static int64 MojoInput_memory_length(MojoInput *io)
{
MojoInputMemInstance *inst = (MojoInputMemInstance *) io->opaque;
return (int64) inst->len;
} // MojoInput_memory_length
static MojoInput *MojoInput_memory_duplicate(MojoInput *io)
{
MojoInputMemInstance *srcinst = (MojoInputMemInstance *) io->opaque;
MojoInput *retval = NULL;
MojoInputMemInstance *inst = NULL;
if (srcinst->refcount != NULL)
{
// we don't copy the data for each duplicate; we just bump a reference
// counter. We free the data when all referencers are closed.
(*srcinst->refcount)++; // !!! FIXME: not thread safe!
} // if
inst = (MojoInputMemInstance*) xmalloc(sizeof (MojoInputMemInstance));
memcpy(inst, srcinst, sizeof (MojoInputMemInstance));
inst->pos = 0;
retval = (MojoInput *) xmalloc(sizeof (MojoInput));
memcpy(retval, io, sizeof (MojoInput));
retval->opaque = inst;
return retval;
} // MojoInput_memory_duplicate
static void MojoInput_memory_close(MojoInput *io)
{
MojoInputMemInstance *inst = (MojoInputMemInstance *) io->opaque;
if (inst->refcount != NULL) // memory we have to free?
{
assert(*inst->refcount > 0);
if (--(*inst->refcount) == 0) // !!! FIXME: not thread safe!
free(inst->ptr);
} // if
free(inst);
free(io);
} // MojoInput_memory_close
MojoInput *MojoInput_newFromMemory(const uint8 *ptr, uint32 len, int constant)
{
MojoInput *io = (MojoInput *) xmalloc(sizeof (MojoInput));
MojoInputMemInstance *inst = (MojoInputMemInstance*)
xmalloc(sizeof (MojoInputMemInstance));
if (constant)
inst->data = ptr;
else
{
inst->ptr = xmalloc(len + sizeof (uint32));
inst->refcount = (uint32 *) inst->ptr;
inst->data = ((const uint8 *) inst->ptr) + sizeof (uint32);
*inst->refcount = 1;
memcpy((void *) inst->data, ptr, len);
} // else
inst->len = len;
io->ready = MojoInput_memory_ready;
io->read = MojoInput_memory_read;
io->seek = MojoInput_memory_seek;
io->tell = MojoInput_memory_tell;
io->length = MojoInput_memory_length;
io->duplicate = MojoInput_memory_duplicate;
io->close = MojoInput_memory_close;
io->opaque = inst;
return io;
} // MojoInput_newFromMemory
429
430
// MojoArchives from directories on the OS filesystem.
431
typedef struct DirStack
432
{
433
void *dir;
434
435
436
437
char *basepath;
struct DirStack *next;
} DirStack;
438
static void pushDirStack(DirStack **_stack, const char *basepath, void *dir)
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
{
DirStack *stack = (DirStack *) xmalloc(sizeof (DirStack));
stack->dir = dir;
stack->basepath = xstrdup(basepath);
stack->next = *_stack;
*_stack = stack;
} // pushDirStack
static void popDirStack(DirStack **_stack)
{
DirStack *stack = *_stack;
if (stack != NULL)
{
DirStack *next = stack->next;
if (stack->dir)
454
MojoPlatform_closedir(stack->dir);
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
free(stack->basepath);
free(stack);
*_stack = next;
} // if
} // popDirStack
static void freeDirStack(DirStack **_stack)
{
while (*_stack)
popDirStack(_stack);
} // freeDirStack
typedef struct
{
DirStack *dirs;
471
char *base;
472
473
} MojoArchiveDirInstance;
474
static boolean MojoArchive_dir_enumerate(MojoArchive *ar)
475
476
{
MojoArchiveDirInstance *inst = (MojoArchiveDirInstance *) ar->opaque;
477
void *dir = NULL;
478
479
freeDirStack(&inst->dirs);
480
MojoArchive_resetEntry(&ar->prevEnum);
481
482
dir = MojoPlatform_opendir(inst->base);
483
if (dir != NULL)
484
pushDirStack(&inst->dirs, inst->base, dir);
485
486
return (dir != NULL);
487
488
489
} // MojoArchive_dir_enumerate
490
static const MojoArchiveEntry *MojoArchive_dir_enumNext(MojoArchive *ar)
491
{
492
uint16 perms = 0644; //(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
493
char *fullpath = NULL;
494
char *dent = NULL; // "dent" == "directory entry"
495
MojoArchiveDirInstance *inst = (MojoArchiveDirInstance *) ar->opaque;
496
const char *basepath;
497
498
499
MojoArchive_resetEntry(&ar->prevEnum);
500
if (inst->dirs == NULL)
501
502
return NULL;
503
504
basepath = inst->dirs->basepath;
505
506
// if readdir fails, it's end of dir (!!! FIXME: what about i/o failures?)
dent = MojoPlatform_readdir(inst->dirs->dir);
507
508
if (dent == NULL) // end of dir?
{
509
510
popDirStack(&inst->dirs);
return MojoArchive_dir_enumNext(ar); // try higher level in tree.
511
512
} // if
513
514
// MojoPlatform layer shouldn't return "." or ".." paths.
assert((strcmp(dent, ".") != 0) && (strcmp(dent, "..") != 0));
515
516
517
518
fullpath = (char *) xmalloc(strlen(basepath) + strlen(dent) + 2);
sprintf(fullpath, "%s/%s", basepath, dent);
free(dent);
519
520
521
522
523
524
525
526
527
528
529
530
531
532
ar->prevEnum.filename = xstrdup(fullpath + strlen(inst->base) + 1);
ar->prevEnum.filesize = 0;
ar->prevEnum.type = MOJOARCHIVE_ENTRY_UNKNOWN;
// We currently force the perms from physical files, since CDs on
// Linux tend to mark every files as executable and read-only. If you
// want to install something with specific permissions, wrap it in a
// tarball, or use Setup.File.permissions, or return a permissions
// string from Setup.File.filter.
//MojoPlatform_perms(fullpath, &perms);
ar->prevEnum.perms = perms;
if (MojoPlatform_isfile(fullpath))
533
{
534
535
536
ar->prevEnum.type = MOJOARCHIVE_ENTRY_FILE;
ar->prevEnum.filesize = MojoPlatform_filesize(fullpath);
} // if
537
538
539
540
541
542
else if (MojoPlatform_issymlink(fullpath))
{
ar->prevEnum.type = MOJOARCHIVE_ENTRY_SYMLINK;
ar->prevEnum.linkdest = MojoPlatform_readlink(fullpath);
if (ar->prevEnum.linkdest == NULL)
543
{
544
545
546
547
free(fullpath);
return MojoArchive_dir_enumNext(ar);
} // if
} // else if
548
549
550
551
552
553
else if (MojoPlatform_isdir(fullpath))
{
void *dir = MojoPlatform_opendir(fullpath);
ar->prevEnum.type = MOJOARCHIVE_ENTRY_DIR;
if (dir == NULL)
554
{
555
556
557
558
559
560
561
562
563
564
565
free(fullpath);
return MojoArchive_dir_enumNext(ar);
} // if
// push this dir on the stack. Next enum will start there.
pushDirStack(&inst->dirs, fullpath, dir);
} // else if
else
{
assert(false && "possible file i/o error?");
566
} // else
567
568
free(fullpath);
569
return &ar->prevEnum;
570
571
572
573
574
} // MojoArchive_dir_enumNext
static MojoInput *MojoArchive_dir_openCurrentEntry(MojoArchive *ar)
{
575
MojoInput *retval = NULL;
576
MojoArchiveDirInstance *inst = (MojoArchiveDirInstance *) ar->opaque;
577
578
if ((inst->dirs != NULL) && (ar->prevEnum.type == MOJOARCHIVE_ENTRY_FILE))
579
{
580
581
582
char *fullpath = (char *) xmalloc(strlen(inst->base) +
strlen(ar->prevEnum.filename) + 2);
sprintf(fullpath, "%s/%s", inst->base, ar->prevEnum.filename);
583
retval = MojoInput_newFromFile(fullpath);
584
free(fullpath);
585
586
587
} // if
return retval;
588
589
590
591
592
593
} // MojoArchive_dir_openCurrentEntry
static void MojoArchive_dir_close(MojoArchive *ar)
{
MojoArchiveDirInstance *inst = (MojoArchiveDirInstance *) ar->opaque;
594
freeDirStack(&inst->dirs);
595
free(inst->base);
596
free(inst);
597
MojoArchive_resetEntry(&ar->prevEnum);
598
599
600
601
602
603
free(ar);
} // MojoArchive_dir_close
MojoArchive *MojoArchive_newFromDirectory(const char *dirname)
{
604
MojoArchive *ar = NULL;
605
MojoArchiveDirInstance *inst;
606
char *real = MojoPlatform_realpath(dirname);
607
608
609
610
611
612
613
614
if (real == NULL)
return NULL;
if (!MojoPlatform_exists(real, NULL))
return NULL;
if (!MojoPlatform_isdir(real))
615
616
617
return NULL;
inst = (MojoArchiveDirInstance *) xmalloc(sizeof (MojoArchiveDirInstance));
618
inst->base = real;
619
ar = (MojoArchive *) xmalloc(sizeof (MojoArchive));
620
621
622
623
ar->enumerate = MojoArchive_dir_enumerate;
ar->enumNext = MojoArchive_dir_enumNext;
ar->openCurrentEntry = MojoArchive_dir_openCurrentEntry;
ar->close = MojoArchive_dir_close;
624
ar->offsetOfStart = -1; // doesn't mean anything here.
625
ar->opaque = inst;
626
627
628
return ar;
} // MojoArchive_newFromDirectory
629
630
631
632
MojoArchive *GBaseArchive = NULL;
633
const char *GBaseArchivePath = NULL;
634
635
636
MojoArchive *MojoArchive_initBaseArchive(void)
{
637
638
639
640
char *basepath = NULL;
const char *cmd = NULL;
MojoInput *io = NULL;
641
if (GBaseArchive != NULL)
642
643
644
645
return GBaseArchive; // already initialized.
if ((cmd = cmdlinestr("base", "MOJOSETUP_BASE", NULL)) != NULL)
{
646
647
char *real = MojoPlatform_realpath(cmd);
if (real != NULL)
648
{
649
650
651
652
653
654
655
656
if (MojoPlatform_isdir(real))
GBaseArchive = MojoArchive_newFromDirectory(real);
else
{
io = MojoInput_newFromFile(real);
if (io != NULL)
GBaseArchive = MojoArchive_newFromInput(io, real);
} // else
657
658
659
660
661
662
if (GBaseArchive != NULL)
basepath = real;
else
free(real);
} // if
663
664
} // else if
665
666
else
{
667
668
basepath = MojoPlatform_appBinaryPath();
io = MojoInput_newFromFile(basepath);
669
670
if (io != NULL)
671
GBaseArchive = MojoArchive_newFromInput(io, basepath);
672
673
if (GBaseArchive == NULL)
674
675
676
677
678
679
{
// Just use the same directory as the binary instead.
char *ptr = strrchr(basepath, '/');
if (ptr != NULL)
*ptr = '\0';
else
680
681
682
683
684
{
free(basepath); // oh well, try cwd.
basepath = MojoPlatform_currentWorkingDir();
} // else
GBaseArchive = MojoArchive_newFromDirectory(basepath);
685
686
// !!! FIXME: failing this, maybe default.mojosetup?
687
} // if
688
689
} // else
690
691
692
693
694
695
696
if (GBaseArchive == NULL)
{
free(basepath);
basepath = NULL;
} // if
GBaseArchivePath = basepath;
697
698
699
700
701
702
703
704
705
706
707
return GBaseArchive;
} // MojoArchive_initBaseArchive
void MojoArchive_deinitBaseArchive(void)
{
if (GBaseArchive != NULL)
{
GBaseArchive->close(GBaseArchive);
GBaseArchive = NULL;
} // if
708
709
710
free((void *) GBaseArchivePath);
GBaseArchivePath = NULL;
711
712
} // MojoArchive_deinitBaseArchive
713
714
715
// This stub is here if we didn't compile in libfetch...
#if !SUPPORT_URL_HTTP && !SUPPORT_URL_FTP
716
MojoInput *MojoInput_newFromURL(const char *url)
717
{
718
logError("No networking support in this build.");
719
return NULL;
720
} // MojoInput_newFromURL
721
722
#endif
723
// end of fileio.c ...