1
2 /***************************************************************************
3 * __ __ _ ___________ *
4 * \ \ / /| |____ ____| *
5 * \ \ / / | | | | *
6 * \ \ /\ / / | | | | *
7 * \ \/ \/ / | | | | *
8 * \ /\ / | | | | *
9 * \/ \/ |_| |_| *
10 * *
11 * Wiimms ISO Tools *
12 * http://wit.wiimm.de/ *
13 * *
14 ***************************************************************************
15 * *
16 * This file is part of the WIT project. *
17 * Visit http://wit.wiimm.de/ for project details and sources. *
18 * *
19 * Copyright (c) 2009-2013 by Dirk Clemens <wiimm@wiimm.de> *
20 * *
21 ***************************************************************************
22 * *
23 * This program is free software; you can redistribute it and/or modify *
24 * it under the terms of the GNU General Public License as published by *
25 * the Free Software Foundation; either version 2 of the License, or *
26 * (at your option) any later version. *
27 * *
28 * This program is distributed in the hope that it will be useful, *
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
31 * GNU General Public License for more details. *
32 * *
33 * See file gpl-2.0.txt or http://www.gnu.org/licenses/gpl-2.0.txt *
34 * *
35 ***************************************************************************/
36
37 #define _GNU_SOURCE 1
38 #define _XOPEN_SOURCE 1
39
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <sys/time.h>
43 #include <sys/ioctl.h>
44
45 #include <fcntl.h>
46 #include <errno.h>
47 #include <utime.h>
48 #include <ctype.h>
49 #include <dirent.h>
50
51 #if defined(__CYGWIN__)
52 #include <cygwin/fs.h>
53 #include <io.h>
54 #include "winapi.h"
55 #elif defined(__APPLE__)
56 #include <sys/disk.h>
57 #elif defined(__linux__)
58 #include <linux/fs.h>
59 #endif
60
61 #include "lib-std.h"
62 #include "lib-sf.h"
63 #include "wbfs-interface.h"
64
65 ///////////////////////////////////////////////////////////////////////////////
66
67 #if defined(HAVE_FIEMAP) && defined(FS_IOC_FIEMAP)
68 #undef HAVE_FIEMAP
69 #define HAVE_FIEMAP 1
70 #include <linux/fiemap.h>
71 #else
72 #undef HAVE_FIEMAP
73 #define HAVE_FIEMAP 0
74 #endif
75
76 #undef HAVE_FIBMAP
77 #if defined(FIGETBSZ) && defined(FIGETBSZ)
78 #define HAVE_FIBMAP 1
79 #else
80 #define HAVE_FIBMAP 0
81 #endif
82
83 //
84 ///////////////////////////////////////////////////////////////////////////////
85 /////////////// file support ///////////////
86 ///////////////////////////////////////////////////////////////////////////////
87
88 int opt_direct = 0;
89 enumIOMode opt_iomode = IOM__IS_DEFAULT | IOM_FORCE_STREAM;
90
91 //-----------------------------------------------------------------------------
92
ScanIOMode(ccp arg)93 void ScanIOMode ( ccp arg )
94 {
95 const enumIOMode new_io = strtol(optarg,0,0); // [[2do]] error handling
96 opt_iomode = new_io & IOM__IS_MASK;
97 if ( verbose > 0 || opt_iomode != new_io )
98 printf("IO mode set to %#0x.\n",opt_iomode);
99 opt_iomode |= IOM_FORCE_STREAM;
100 }
101
102 ///////////////////////////////////////////////////////////////////////////////
103
GetHSS(int fd,u32 default_value)104 u32 GetHSS ( int fd, u32 default_value )
105 {
106 #ifdef DKIOCGETBLOCKSIZE
107 TRACE(" - try DKIOCGETBLOCKSIZE\n");
108 {
109 unsigned long size32 = 0;
110 if ( ioctl(fd, DKIOCGETBLOCKSIZE, &size32 ) >= 0 && size32 )
111 {
112 TRACE("GetHSS(%d) DKIOCGETBLOCKSIZE := %x = %u\n",fd,size32,size32);
113 return size32;
114 }
115 }
116 #endif
117
118 #ifdef BLKSSZGET
119 TRACE(" - try BLKSSZGET\n");
120 {
121 unsigned long size32 = 0;
122 if ( ioctl(fd, BLKSSZGET, &size32 ) >= 0 && size32 )
123 {
124 TRACE("GetHSS(%d) BLKSSZGET := %lx = %lu\n",fd,size32,size32);
125 return size32;
126 }
127 }
128 #endif
129
130 TRACE("GetHSS(%d) default_value := %x = %u\n",fd,default_value,default_value);
131 return default_value;
132 }
133
134 ///////////////////////////////////////////////////////////////////////////////
135
valid_offset(int fd,off_t off)136 static int valid_offset ( int fd, off_t off )
137 {
138 noTRACE(" - valid_offset(%d,%llu=%#llx)\n",fd,(u64)off,(u64)off);
139 char ch;
140 if ( !off
141 || off > ~(off_t)0 / 2
142 || lseek(fd,off,SEEK_SET) == (off_t)-1 )
143 return 0;
144 return read(fd,&ch,1) == 1;
145 }
146
147 //-----------------------------------------------------------------------------
148
GetBlockDevSize(int fd)149 off_t GetBlockDevSize ( int fd )
150 {
151 TRACE("GetBlockDevSize(%d)\n",fd);
152
153 #ifdef BLKGETSIZE64
154 TRACE(" - try BLKGETSIZE64\n");
155 {
156 unsigned long long size64 = 0;
157 if ( ioctl(fd, BLKGETSIZE64, &size64 ) >= 0 && size64 )
158 return size64;
159 }
160 #endif
161
162 #ifdef DKIOCGETBLOCKCOUNT
163 TRACE(" - try DKIOCGETBLOCKCOUNT\n");
164 {
165 unsigned long long size64 = 0;
166 if ( ioctl(fd, DKIOCGETBLOCKCOUNT, &size64 ) >= 0 && size64 )
167 return size64 * GetHSS(fd,HD_SECTOR_SIZE);
168 }
169 #endif
170
171 #ifdef BLKGETSIZE
172 TRACE(" - try BLKGETSIZE\n");
173 {
174 unsigned long size32 = 0;
175 if ( ioctl(fd, BLKGETSIZE, &size32 ) >= 0 && size32 )
176 return (off_t)size32 * GetHSS(fd,HD_SECTOR_SIZE);
177 }
178 #endif
179
180 TRACE(" - try lseek(SEEK_END)\n");
181 {
182 off_t off = 0;
183 off = lseek(fd,0,SEEK_END);
184 if ( off && off != (off_t)-1 )
185 {
186 lseek(fd,0,SEEK_SET);
187 return off;
188 }
189 }
190
191 TRACE(" - try binary search\n");
192 off_t low, high;
193 for ( low = 0, high = GiB; high > low && valid_offset(fd,high); high *= 2 )
194 low = high;
195 TRACE(" - low=%llx, high=%llx\n",(u64)low,(u64)high);
196
197 while ( low < high - 1 )
198 {
199 const off_t mid = (low + high) / 2;
200 if (valid_offset(fd,mid))
201 low = mid;
202 else
203 high = mid;
204 }
205 lseek(fd,0,SEEK_SET);
206 return low ? low + 1 : 0;
207 }
208
209 ///////////////////////////////////////////////////////////////////////////////
210
StatFile(struct stat * st,ccp fname,int fd)211 enumError StatFile ( struct stat * st, ccp fname, int fd )
212 {
213 ASSERT(st);
214
215 TRACE("StatFile(%s,%d)\n",fname?fname:"",fd);
216
217 // normalize parameters
218 if ( fname && *fname )
219 fd = -1;
220 else
221 fname = 0;
222
223 if ( fname ? stat(fname,st) : fstat(fd,st) )
224 {
225 memset(st,0,sizeof(*st));
226 return ERR_WARNING;
227 }
228
229 TRACE(" - st_dev: %llu\n",(u64)st->st_dev);
230 TRACE(" - st_ino: %llu\n",(u64)st->st_ino);
231 TRACE(" - st_mode: %llx [REG=%u,DIR=%d,CHR=%d,BLK=%d,FIFO=%d,LNK=%d,SOCK=%d]\n",
232 (u64)st->st_mode, S_ISREG(st->st_mode),
233 S_ISDIR(st->st_mode), S_ISCHR(st->st_mode),
234 S_ISBLK(st->st_mode), S_ISFIFO(st->st_mode),
235 S_ISLNK(st->st_mode), S_ISSOCK(st->st_mode) );
236 TRACE(" - st_nlink: %llu\n",(u64)st->st_nlink);
237 TRACE(" - st_uid: %llu\n",(u64)st->st_uid);
238 TRACE(" - st_gid: %llu\n",(u64)st->st_gid);
239 TRACE(" - st_rdev: %llu\n",(u64)st->st_rdev);
240 TRACE(" - st_size: %10llx = %llu\n",(u64)st->st_size,(u64)st->st_size);
241 TRACE(" - st_blksize: %10llx = %llu\n",(u64)st->st_blksize,(u64)st->st_blksize);
242 TRACE(" - st_blocks: %10llx = %llu\n",(u64)st->st_blocks,(u64)st->st_blocks);
243 TRACE(" - st_atime: %llu\n",(u64)st->st_atime);
244 TRACE(" - st_mtime: %llu\n",(u64)st->st_mtime);
245 TRACE(" - st_ctime: %llu\n",(u64)st->st_ctime);
246
247 if ( !st->st_size && GetFileMode(st->st_mode) != FM_OTHER )
248 {
249 if (fname)
250 fd = open(fname,O_RDONLY);
251
252 if ( fd != -1 )
253 {
254 if ( GetFileMode(st->st_mode) > FM_PLAIN )
255 {
256 st->st_size = GetBlockDevSize(fd);
257 TRACE(" + st_size: %llu\n",(u64)st->st_size);
258 }
259
260 if (!st->st_size)
261 {
262 st->st_size = lseek(fd,0,SEEK_END);
263 TRACE(" + st_size: %llu\n",(u64)st->st_size);
264 lseek(fd,0,SEEK_SET);
265 }
266
267 if (fname)
268 close(fd);
269 }
270 }
271
272 return ERR_OK;
273 }
274
275 ///////////////////////////////////////////////////////////////////////////////
276 ///////////////////////////////////////////////////////////////////////////////
277 // initialize, reset and close files
278
InitializeFile(File_t * f)279 void InitializeFile ( File_t * f )
280 {
281 TRACE("#F# InitializeFile(%p)\n",f);
282 ASSERT(f);
283 ASSERT(ERR_OK==0);
284
285 memset(f,0,sizeof(*f));
286 f->fd = -1;
287 f->sector_size = 512;
288 f->fname = EmptyString;
289 f->split_fname_format = EmptyString;
290 f->slot = -1;
291 f->already_created_mode = 2;
292
293 // normalize 'opt_iomode'
294 opt_iomode = opt_iomode & IOM__IS_MASK | IOM_FORCE_STREAM;
295
296 #ifdef __CYGWIN__
297 opt_iomode |= IOM_IS_WBFS_PART;
298 #endif
299 }
300
301 ///////////////////////////////////////////////////////////////////////////////
302
XResetFile(XPARM File_t * f,bool remove_file)303 enumError XResetFile ( XPARM File_t * f, bool remove_file )
304 {
305 TRACE("#F# ResetFile(%p,%d)\n",f,remove_file);
306 ASSERT(f);
307 enumError stat = XClearFile( XCALL f, remove_file );
308
309 // save user settungs
310 const bool open_flags = f->open_flags;
311 const bool disable_errors = f->disable_errors;
312 const bool create_directory = f->create_directory;
313 const bool allow_direct_io = f->allow_direct_io;
314 const int already_created_mode = f->already_created_mode;
315
316 InitializeFile(f);
317
318 // restore user settings
319 f->open_flags = open_flags;
320 f->disable_errors = disable_errors;
321 f->create_directory = create_directory;
322 f->allow_direct_io = allow_direct_io;
323 f->already_created_mode = already_created_mode;
324
325 return stat;
326 }
327
328 ///////////////////////////////////////////////////////////////////////////////
329
XClearFile(XPARM File_t * f,bool remove_file)330 enumError XClearFile ( XPARM File_t * f, bool remove_file )
331 {
332 ASSERT(f);
333 TRACE("#F# ClearFile(%p,%d) fd=%d fp=%p\n",f,remove_file,f->fd,f->fp);
334
335 enumError err = ERR_OK;
336 if (f->split_f)
337 {
338 File_t **end, **ptr = f->split_f;
339 for ( end = ptr + f->split_used; ptr < end; ptr++ )
340 {
341 enumError err1 = XClearFile( XCALL *ptr, remove_file );
342 FREE(*ptr);
343 if ( err < err1 )
344 err = err1;
345 }
346 FREE(f->split_f);
347 f->split_f = 0;
348 remove_file = false;
349 };
350 f->split_used = 0;
351
352 enumError err1 = XCloseFile( XCALL f, remove_file );
353 if ( err < err1 )
354 err = err1;
355
356 FreeString(f->fname); f->fname = EmptyString;
357 FreeString(f->path); f->path = 0;
358 FreeString(f->rename); f->rename = 0;
359 FreeString(f->outname); f->outname = 0;
360 FreeString(f->split_fname_format); f->split_fname_format = EmptyString;
361 FreeString(f->split_rename_format); f->split_rename_format = 0;
362
363 ASSERT(!f->is_caching);
364 ASSERT(!f->cache);
365 ASSERT(!f->cur_cache);
366
367 f->last_error = err;
368 if ( f->max_error < err )
369 f->max_error = err;
370 return err;
371 }
372
373 ///////////////////////////////////////////////////////////////////////////////
374
XCloseFile(XPARM File_t * f,bool remove_file)375 enumError XCloseFile ( XPARM File_t * f, bool remove_file )
376 {
377 DASSERT(f);
378 TRACE("#F# CloseFile(%p,%d) fd=%d fp=%p\n",f,remove_file,f->fd,f->fp);
379
380 //----- pralloc support
381
382 if ( !remove_file && f->prealloc_size > f->max_off && IsOpenF(f) )
383 {
384 PRINT("PREALLOC/CLOSE: pre=%llx, max=%llx \n",
385 (u64)f->prealloc_size, (u64)f->max_off );
386 XSetSizeF(XCALL f,f->max_off);
387 }
388
389
390 //----- close file
391
392 bool close_err = false;
393 if ( f->fp )
394 close_err = fclose(f->fp) != 0;
395 else if ( f->fd != -1 )
396 close_err = close(f->fd) != 0;
397
398 f->fp = 0;
399 f->fd = -1;
400
401 enumError err = ERR_OK;
402 if (close_err)
403 {
404 err = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
405 if (!f->disable_errors)
406 PrintError( XERROR1, err,
407 "Close file failed: %s\n", f->fname );
408 }
409
410
411 //----- split file support & removing & renaming
412
413 if (f->split_f)
414 {
415 File_t **end, **ptr = f->split_f;
416 for ( end = ptr + f->split_used; ptr < end; ptr++ )
417 {
418 TRACE("#S#%zd# close %s\n",ptr-f->split_f,(*ptr)->fname);
419 enumError err1 = XCloseFile( XCALL *ptr, remove_file );
420 if ( err < err1 )
421 err = err1;
422 }
423 }
424 else if ( !f->is_stdfile && S_ISREG(f->st.st_mode) )
425 {
426 ccp * path = f->path ? &f->path : &f->fname;
427 if ( remove_file && *path && **path )
428 {
429 TRACE("REMOVE: %s\n",*path);
430 unlink(*path);
431 FreeString(f->rename);
432 f->rename = 0;
433 }
434 else if (f->rename)
435 {
436 TRACE("RENAME: %s\n",*path);
437 TRACE(" TO: %s\n",f->rename);
438 if (rename(*path,f->rename))
439 {
440 if (!f->disable_errors)
441 ERROR1( ERR_CANT_CREATE,
442 "Can't create file: %s\n", f->rename );
443 if ( err < ERR_CANT_CREATE )
444 err = ERR_CANT_CREATE;
445 unlink(*path);
446 }
447
448 FreeString(*path);
449 *path = f->rename;
450 f->rename = 0;
451 }
452 }
453
454
455 //----- clean
456
457 ClearCache(f);
458 ResetMemMap(&f->prealloc_map);
459
460 f->cur_off = f->file_off = 0;
461
462 f->last_error = err;
463 if ( f->max_error < err )
464 f->max_error = err;
465
466 return err;
467 }
468
469 ///////////////////////////////////////////////////////////////////////////////
470
XSetFileTime(XPARM File_t * f,FileAttrib_t * set_time)471 enumError XSetFileTime ( XPARM File_t * f, FileAttrib_t * set_time )
472 {
473 // try to change time and ignore error messages
474
475 ASSERT(f);
476
477 enumError err = ERR_OK;
478 if ( set_time && set_time->mtime )
479 {
480 err = XCloseFile( XCALL f, false );
481
482 struct timeval tval[2];
483 tval[0].tv_sec = set_time->atime ? set_time->atime : set_time->mtime;
484 tval[1].tv_sec = set_time->mtime;
485 tval[0].tv_usec = tval[1].tv_usec = 0;
486
487 if (f->split_f)
488 {
489 File_t **end, **ptr = f->split_f;
490 for ( end = ptr + f->split_used; ptr < end; ptr++ )
491 {
492 TRACE("XSetFileTime(%p,%p) fname=%s\n",f,set_time,(*ptr)->fname);
493 utimes((*ptr)->fname,tval);
494 }
495 }
496 else
497 {
498 TRACE("XSetFileTime(%p,%p) fname=%s\n",f,set_time,f->fname);
499 utimes(f->fname,tval);
500 }
501 }
502 return err;
503 }
504
505 ///////////////////////////////////////////////////////////////////////////////
506 ///////////////////////////////////////////////////////////////////////////////
507
XOpenFileHelper(XPARM File_t * f,enumIOMode iomode,int default_flags,int force_flags)508 static enumError XOpenFileHelper
509 ( XPARM File_t * f, enumIOMode iomode, int default_flags, int force_flags )
510 {
511 ASSERT(f);
512 TRACE("#F# OpenFileHelper(%p,%x,%x,%x) dis=%d, mkdir=%d, fname=%s\n",
513 f, iomode, default_flags, force_flags,
514 f->disable_errors, f->create_directory, f->fname );
515
516 #ifdef O_LARGEFILE
517 TRACE("FORCE O_LARGEFILE\n");
518 force_flags |= O_LARGEFILE;
519 #endif
520
521 #ifdef O_DIRECT
522 TRACE("FORCE O_DIRECT = %d,%d\n",opt_direct,f->allow_direct_io);
523 if ( opt_direct && f->allow_direct_io )
524 force_flags |= O_DIRECT;
525 #endif
526
527 f->active_open_flags = ( f->open_flags ? f->open_flags : default_flags )
528 | force_flags;
529
530 const int mode_mask = O_RDONLY|O_WRONLY|O_RDWR;
531 int mode = f->active_open_flags & mode_mask;
532 if ( !mode || mode == O_RDONLY )
533 {
534 mode = O_RDONLY;
535 f->is_reading = true;
536 }
537 else if ( mode == O_WRONLY )
538 {
539 f->is_writing = true;
540 }
541 else
542 {
543 mode = O_RDWR;
544 f->is_reading = true;
545 f->is_writing = true;
546 }
547 f->active_open_flags = f->active_open_flags & ~mode_mask | mode;
548
549 if ( f->fd == -1 )
550 {
551 TRACE("open %s, create-dir=%d\n",f->fname,f->create_directory);
552 #ifdef __CYGWIN__
553 char * temp = AllocNormalizedFilenameCygwin(f->fname);
554 TRACE("open %p %p %s\n",f->fname,temp,temp);
555 //FreeString(f->fname); // [[2do]] [memleak] -> forces a core dump -- mhhm
556 f->fname = temp;
557 #endif
558 f->fd = open( f->fname, f->active_open_flags, 0666 );
559 if ( f->fd == -1 && f->create_directory && f->is_writing )
560 {
561 const enumError err = CreatePath(f->fname);
562 if (err)
563 return err;
564 f->fd = open( f->fname, f->active_open_flags, 0666 );
565 }
566 }
567 TRACE("#F# OpenFile '%s' fd=%d, dis=%d\n", f->fname, f->fd, f->disable_errors );
568
569 if ( f->fd == -1 )
570 {
571 if ( f->active_open_flags & O_CREAT )
572 {
573 f->max_error = f->last_error = ERR_CANT_CREATE;
574 if (!f->disable_errors)
575 PrintError( XERROR1, f->last_error,
576 "Can't create file: %s\n",f->fname);
577 }
578 else
579 {
580 f->max_error = f->last_error = ERR_CANT_OPEN;
581 if (!f->disable_errors)
582 PrintError( XERROR1, f->last_error,
583 "Can't open file: %s\n",f->fname);
584 }
585 }
586 else if (StatFile(&f->st,0,f->fd))
587 {
588 close(f->fd);
589 f->fd = -1;
590 f->max_error = f->last_error = ERR_READ_FAILED;
591 if (!f->disable_errors)
592 PrintError( XERROR1, f->last_error,"Can't stat file: %s\n",f->fname);
593 }
594 else
595 {
596 CopyFileAttribStat(&f->fatt,&f->st,false);
597 f->seek_allowed = GetFileMode(f->st.st_mode) != FM_OTHER
598 && lseek(f->fd,0,SEEK_SET) != (off_t)-1;
599 if ( iomode & opt_iomode || S_ISCHR(f->st.st_mode) )
600 XOpenStreamFile(XCALL f);
601 }
602
603 TRACE("#F# OpenFileHelper(%p) returns %d, fd=%d, fp=%p, seek-allowed=%d, rw=%d,%d\n",
604 f, f->last_error, GetFD(f), GetFP(f), f->seek_allowed,
605 f->is_reading, f->is_writing );
606 return f->last_error;
607 }
608
609 ///////////////////////////////////////////////////////////////////////////////
610
XOpenFile(XPARM File_t * f,ccp fname,enumIOMode iomode)611 enumError XOpenFile ( XPARM File_t * f, ccp fname, enumIOMode iomode )
612 {
613 ASSERT(f);
614 TRACE("XOpenFile(%s)\n",fname);
615
616 const bool no_fname = !fname;
617 if (no_fname)
618 {
619 fname = f->fname;
620 f->fname = 0;
621 }
622
623 XResetFile( XCALL f, false );
624
625 f->is_stdfile = fname[0] == '-' && !fname[1];
626 if (f->is_stdfile)
627 {
628 f->fd = dup(fileno(stdin));
629 f->fname = STRDUP("- (stdin)");
630 }
631 else
632 f->fname = no_fname ? fname : STRDUP(fname);
633
634 return XOpenFileHelper(XCALL f,iomode,O_RDONLY,O_RDONLY);
635 }
636
637 ///////////////////////////////////////////////////////////////////////////////
638
XOpenFileModify(XPARM File_t * f,ccp fname,enumIOMode iomode)639 enumError XOpenFileModify ( XPARM File_t * f, ccp fname, enumIOMode iomode )
640 {
641 ASSERT(f);
642
643 const bool no_fname = !fname;
644 if (no_fname)
645 {
646 fname = f->fname;
647 f->fname = 0;
648 }
649
650 XResetFile( XCALL f, false );
651
652 f->fname = no_fname ? fname : STRDUP(fname);
653 return XOpenFileHelper(XCALL f,iomode,O_RDWR,O_RDWR);
654 }
655
656 ///////////////////////////////////////////////////////////////////////////////
657
XCheckCreated(XPARM ccp fname,bool disable_errors,enumError err_code)658 enumError XCheckCreated
659 ( XPARM ccp fname, bool disable_errors, enumError err_code )
660 {
661 if (!InsertStringField(&created_files,fname,false))
662 {
663 if (!disable_errors)
664 PrintError( XERROR0, err_code,
665 "File already created: %s\n", fname );
666 return err_code;
667 }
668 return ERR_OK;
669 }
670
671 ///////////////////////////////////////////////////////////////////////////////
672
XCreateFile(XPARM File_t * f,ccp fname,enumIOMode iomode,int overwrite)673 enumError XCreateFile
674 ( XPARM File_t * f, ccp fname, enumIOMode iomode, int overwrite )
675 {
676 ASSERT(f);
677
678 const int open_flags = O_CREAT|O_WRONLY|O_TRUNC|O_EXCL;
679 const int force_flags = O_CREAT;
680
681 if (!fname)
682 {
683 fname = f->fname;
684 f->fname = 0;
685 }
686
687 XResetFile( XCALL f, false );
688
689 f->is_stdfile = fname[0] == '-' && !fname[1];
690 if (f->is_stdfile)
691 {
692 f->fd = dup(fileno(stdout));
693 f->fname = STRDUP("- (stdout)");
694
695 return XOpenFileHelper(XCALL f, iomode, open_flags, force_flags );
696 }
697
698 if (!stat(fname,&f->st))
699 {
700 if ( overwrite < 0 || f->disable_errors )
701 return ERR_ALREADY_EXISTS;
702
703 if ( S_ISBLK(f->st.st_mode) || S_ISCHR(f->st.st_mode) )
704 {
705 if (!overwrite)
706 return PrintError( XERROR0, ERR_ALREADY_EXISTS,
707 "Can't overwrite block device: %s\n", fname );
708
709 f->fname = STRDUP(fname);
710 return XOpenFileHelper(XCALL f, iomode, O_WRONLY, 0 );
711 }
712 else
713 {
714 if (!S_ISREG(f->st.st_mode))
715 return PrintError( XERROR0, ERR_WRONG_FILE_TYPE,
716 "Not a plain file: %s\n", fname );
717
718 if (!overwrite)
719 return PrintError( XERROR0, ERR_ALREADY_EXISTS,
720 "File already exists: %s\n", fname );
721 }
722 }
723
724 TRACE("#F# CREATE: '%s' mkdir=%d\n",fname,f->create_directory);
725
726 int flen = strlen(fname);
727 if ( flen >= PATH_MAX )
728 {
729 // no temp name possible
730
731 f->fname = STRDUP(fname);
732 return XOpenFileHelper(XCALL f, iomode, open_flags, force_flags );
733 }
734
735 const bool disable_errors = f->disable_errors;
736 TRACE("dis=%d\n",disable_errors);
737 f->disable_errors = true;
738
739 char fbuf[PATH_MAX+20], *XXXXXX;
740 if ( flen >= 7 && !memcmp(fname+flen-7,".XXXXXX",7) )
741 {
742 memcpy(fbuf,fname,flen);
743 fbuf[flen] = 0;
744 XXXXXX = fbuf + flen - 6;
745 }
746 else if ( flen >= 11 && !memcmp(fname+flen-11,".XXXXXX.tmp",11) )
747 {
748 memcpy(fbuf,fname,flen);
749 fbuf[flen] = 0;
750 XXXXXX = fbuf + flen - 10;
751 }
752 else
753 {
754 if ( f->already_created_mode > 1 )
755 {
756 if (XCheckCreated(XCALL fname,disable_errors,ERR_CANT_CREATE))
757 goto abort;
758 }
759 else if ( f->already_created_mode > 0 )
760 XCheckCreated(XCALL fname,disable_errors,ERR_WARNING);
761
762 f->rename = STRDUP(fname);
763
764 ccp name = strrchr(fname,'/');
765 name = name ? name+1 : fname;
766 memcpy(fbuf,fname,name-fname);
767 char * dest = fbuf + (name-fname);
768 ASSERT( dest < fbuf + PATH_MAX );
769
770 char * dest_name = dest;
771 *dest++ = '.';
772 dest = StringCopyE(dest,fbuf+PATH_MAX,name);
773 if ( dest - dest_name > 50 )
774 dest = dest_name + 50;
775 XXXXXX = dest + 1;
776 StringCopyE(dest,fbuf+sizeof(fbuf),".XXXXXX.tmp");
777 TRACE("#F# TEMP: '%s'\n",fbuf);
778 }
779
780
781 //---------------------------------------------------------
782 // I have seen the glibc function __gen_tempname() ;)
783 //---------------------------------------------------------
784
785 struct timeval tval;
786 gettimeofday(&tval,NULL);
787 u64 random_time_bits = (u64) tval.tv_usec << 16 ^ tval.tv_sec;
788 u64 value = random_time_bits ^ getpid();
789
790 static char letters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
791 "abcdefghijklmnopqrstuvwxyz"
792 "0123456789";
793
794 int i;
795 for ( i = 0; i < 100; value += 7777, i++ )
796 {
797 u64 v = value;
798 XXXXXX[0] = letters[v%62]; v /= 62;
799 XXXXXX[1] = letters[v%62]; v /= 62;
800 XXXXXX[2] = letters[v%62]; v /= 62;
801 XXXXXX[3] = letters[v%62]; v /= 62;
802 XXXXXX[4] = letters[v%62]; v /= 62;
803 XXXXXX[5] = letters[v%62];
804
805 f->fname = fbuf; // temporary assignment
806 const enumError err = XOpenFileHelper(XCALL f,iomode,open_flags,open_flags);
807 if ( err == ERR_OK || err == ERR_CANT_CREATE_DIR )
808 {
809 noPRINT("#F# TEMP: '%s'\n",fbuf);
810 f->fname = STRDUP(fbuf);
811 f->disable_errors = disable_errors;
812 return f->last_error = f->max_error = err;
813 }
814 f->fname = 0;
815 XResetFile( XCALL f, false );
816 }
817
818 // cleanup
819 f->fname = EmptyString;
820 memset(&f->st,0,sizeof(f->st));
821
822 if (!disable_errors)
823 PrintError( XERROR0, ERR_CANT_CREATE,
824 "Can't create temp file: %s\n", fname );
825
826 abort:
827 f->max_error = f->last_error = ERR_CANT_CREATE;
828 f->disable_errors = disable_errors;
829 TRACE("#F# CreateFile(%p) returns %d, fd=%d, fp=%p\n",
830 f, f->last_error, f->fd, f->fp );
831 return f->last_error;
832 }
833
834 ///////////////////////////////////////////////////////////////////////////////
835
XOpenStreamFile(XPARM File_t * f)836 enumError XOpenStreamFile ( XPARM File_t * f )
837 {
838 ASSERT(f);
839 TRACE("#F# OpenStreamFile(%p) fd=%d, fp=%p\n",f,f->fd,f->fp);
840
841 f->last_error = 0;
842 if ( f->fd != -1 || !f->fp )
843 {
844 // flag 'b' is set for compatibilitiy only, linux ignores it
845 ccp mode = !f->is_writing ? "rb" : f->is_reading ? "r+b" : "wb";
846 TRACE("#F# open mode: '%s'\n",mode);
847 f->fp = fdopen(f->fd,mode);
848 if (!f->fp)
849 {
850 f->last_error = ERR_CANT_OPEN;
851 if ( f->max_error < f->last_error )
852 f->max_error = f->last_error;
853 if (!f->disable_errors)
854 PrintError( XERROR1, f->last_error, "Can't open file stream: %s\n",
855 f->fname );
856 }
857 else
858 SeekF(f,f->file_off);
859 }
860
861 noTRACE("#F# OpenStreamFile(%p) returns %d, fd=%d, fp=%p, off=%llx\n",
862 f, f->last_error, GetFD(f), GetFP(f), (u64)f->file_off );
863 return f->last_error;
864 }
865
866 //
867 ///////////////////////////////////////////////////////////////////////////////
868 ///////////////////////////////////////////////////////////////////////////////
869 // file name generation
870
NormalizeFileName(char * buf,char * end,ccp source,bool allow_slash)871 char * NormalizeFileName ( char * buf, char * end, ccp source, bool allow_slash )
872 {
873 ASSERT(buf);
874 ASSERT(end);
875 ASSERT( buf <= end );
876 char * dest = buf;
877 TRACE("NormalizeFileName(%s,%d)\n",source,allow_slash);
878
879 if (source)
880 {
881 #ifdef __CYGWIN__
882 if (allow_slash)
883 {
884 const int drv_len = IsWindowsDriveSpec(source);
885 if (drv_len)
886 {
887 dest = StringCopyE(dest,end,"/cygdrive/c/");
888 if ( dest < end )
889 dest[-2] = tolower((int)*source);
890 source += drv_len;
891 }
892 }
893 #endif
894
895 bool skip_space = true;
896 while ( *source && dest < end )
897 {
898 unsigned char ch = *source++;
899 if ( ch == ':' )
900 {
901 if (!skip_space)
902 *dest++ = ' ';
903 if ( dest + 2 <= end )
904 {
905 *dest++ = '-';
906 *dest++ = ' ';
907 }
908 skip_space = true;
909 }
910 else
911 {
912 if ( isalnum(ch)
913 || ( use_utf8
914 ? ch >= 0x80
915 : ch == 0xe4 // ä
916 || ch == 0xf6 // ö
917 || ch == 0xfc // ü
918 || ch == 0xdf // ß
919 || ch == 0xc4 // A
920 || ch == 0xd6 // Ö
921 || ch == 0xdc // Ü
922 )
923 || strchr("_+-=%'\"$%&,.!()[]{}<>",ch)
924 || ch == '/' && allow_slash )
925 {
926 *dest++ = ch;
927 skip_space = false;
928 }
929 #ifdef __CYGWIN__
930 else if ( ch == '\\' && allow_slash )
931 {
932 *dest++ = '/';
933 skip_space = false;
934 }
935 #endif
936 else if (!skip_space)
937 {
938 *dest++ = ' ';
939 skip_space = true;
940 }
941 }
942 }
943 }
944 if ( dest > buf && dest[-1] == ' ' )
945 dest--;
946
947 ASSERT( dest <= end );
948 return dest;
949 }
950
951 ///////////////////////////////////////////////////////////////////////////////
952
ReduceToPathAndType(char * buf,uint buf_size,ccp fname)953 uint ReduceToPathAndType
954 (
955 char *buf, // valid return buffer
956 uint buf_size, // size of 'buf'
957 ccp fname // source: file name
958 )
959 {
960 DASSERT(buf);
961 DASSERT(buf_size>1);
962 DASSERT(fname);
963
964 StringCopyS(buf,buf_size,fname);
965 char *dest = strrchr(buf,'/');
966 dest = dest ? dest+1 : buf;
967 ccp src = strrchr(fname,'.');
968 if ( src && src > fname )
969 {
970 *dest++ = '*';
971 while ( ( *dest++ = *src++ ) != 0 )
972 ;
973 DASSERT( dest < buf + buf_size );
974 *dest = 0;
975 return buf - dest;
976 }
977
978 *buf = 0;
979 return 0;
980 }
981
982 ///////////////////////////////////////////////////////////////////////////////
983
SetFileName(File_t * f,ccp source,bool allow_slash)984 void SetFileName ( File_t * f, ccp source, bool allow_slash )
985 {
986 char fbuf[PATH_MAX];
987 char * dest = NormalizeFileName(fbuf, fbuf+sizeof(fbuf)-1, source, allow_slash );
988 *dest++ = 0;
989 const int len = dest - fbuf;
990
991 FreeString(f->fname);
992 if ( len <= 1 )
993 f->fname = EmptyString;
994 else
995 {
996 dest = MALLOC(len);
997 memcpy(dest,fbuf,len);
998 f->fname = dest;
999 }
1000 }
1001
1002 ///////////////////////////////////////////////////////////////////////////////
1003
GenFileName(File_t * f,ccp path,ccp name,ccp title,ccp id6,ccp ext)1004 void GenFileName ( File_t * f, ccp path, ccp name, ccp title, ccp id6, ccp ext )
1005 {
1006 ASSERT(f);
1007
1008 TRACE("GenFileName(,p=%s,n=%s,i=%s,e=%s)\n",
1009 path ? path : "",
1010 name ? name : "",
1011 id6 ? id6 : "",
1012 ext ? ext : "" );
1013
1014 FreeString(f->fname);
1015 f->fname = EmptyString;
1016
1017 //----- check stdout
1018
1019 f->is_stdfile = name && name[0] == '-' && !name[1];
1020 if (f->is_stdfile)
1021 {
1022 f->fname = MinusString;
1023 return;
1024 }
1025
1026 //----- evaluate 'path'
1027
1028 char fbuf[PATH_MAX+20];
1029
1030 if ( name && *name == '/' )
1031 path = 0;
1032
1033 int plen = path ? strlen(path) : 0;
1034 if ( plen > PATH_MAX )
1035 {
1036 if (!realpath(path,fbuf))
1037 strcpy(fbuf,"./");
1038 plen = strlen(fbuf);
1039 }
1040 else if (plen)
1041 memcpy(fbuf,path,plen);
1042
1043 char * dest = fbuf + plen;
1044 if ( plen && dest[-1] != '/' )
1045 *dest++ = '/';
1046
1047 TRACE(" >PATH: |%.*s|\n",(int)(dest-fbuf),fbuf);
1048
1049 //----- evaluate 'name'
1050
1051 char * end = fbuf + sizeof(fbuf) - 15; // space for "[123456].ext"
1052
1053 if (name)
1054 {
1055 // just copy it
1056 dest = StringCopyE(dest,end,name);
1057 TRACE(" >NAME: |%.*s|\n",(int)(dest-fbuf),fbuf);
1058 }
1059 else
1060 {
1061 dest = NormalizeFileName(dest,end,title,false);
1062 TRACE(" >TITL: |%.*s|\n",(int)(dest-fbuf),fbuf);
1063
1064 if ( id6 && *id6 )
1065 {
1066 dest += snprintf(dest,end-dest,"%s[%s]",
1067 dest > fbuf && dest[-1] == '/' ? "" : " ", id6 );
1068 TRACE(" >ID6: |%.*s|\n",(int)(dest-fbuf),fbuf);
1069 }
1070 }
1071
1072 //----- ext handling
1073
1074 if (ext)
1075 {
1076 const int elen = strlen(ext);
1077 if ( elen > 0
1078 && ( dest - elen <= fbuf || memcmp(dest-elen,ext,elen) )
1079 && dest + elen < end )
1080 {
1081 TRACE("add ext %s\n",ext);
1082 memcpy(dest,ext,elen);
1083 dest += elen;
1084 TRACE(" >EXT: |%.*s|\n",(int)(dest-fbuf),fbuf);
1085 }
1086 }
1087
1088 //----- term
1089
1090 *dest = 0;
1091 f->fname = STRDUP(fbuf);
1092 TRACE("FNAME: %s\n",fbuf);
1093 }
1094
1095 ///////////////////////////////////////////////////////////////////////////////
1096
GenDestFileName(File_t * f,ccp dest,ccp default_name,ccp ext,bool rm_std_ext)1097 void GenDestFileName
1098 ( File_t * f, ccp dest, ccp default_name, ccp ext, bool rm_std_ext )
1099 {
1100 char fbuf[PATH_MAX];
1101 if ( rm_std_ext && default_name )
1102 {
1103 const size_t name_len = strlen(default_name);
1104 bool done = false;
1105 enumOFT oft;
1106 for ( oft = 0; oft < OFT__N && !done; oft++ )
1107 {
1108 int i;
1109 for ( i = 0; i < 2 && !done; i++ )
1110 {
1111 ccp rm = i ? oft_info[oft].ext2 : oft_info[oft].ext1;
1112 if (!rm)
1113 continue;
1114
1115 const size_t rm_len = strlen(rm);
1116 const size_t cut_len = name_len - rm_len;
1117 if ( rm_len && rm_len <= name_len && !strcasecmp(rm,default_name+cut_len) )
1118 {
1119 // got it!
1120
1121 if ( cut_len < sizeof(fbuf) )
1122 {
1123 memcpy(fbuf,default_name,cut_len);
1124 fbuf[cut_len] = 0;
1125 default_name = fbuf;
1126 }
1127 done = true; // remove maximal 1 extension
1128 }
1129 }
1130 }
1131 }
1132
1133 if ( IsDirectory(dest,true) )
1134 {
1135 if ( dest && *dest && default_name )
1136 {
1137 ccp file_part = strrchr(default_name,'/');
1138 if ( file_part )
1139 default_name = file_part + 1;
1140 }
1141 GenFileName(f,dest,default_name,0,0,ext);
1142 }
1143 else
1144 GenFileName(f,0,dest,0,0,0);
1145 }
1146
1147 ///////////////////////////////////////////////////////////////////////////////
1148
GenImageFileName(File_t * f,ccp dest,ccp default_name,enumOFT oft)1149 void GenImageFileName ( File_t * f, ccp dest, ccp default_name, enumOFT oft )
1150 {
1151 ccp ext = (uint)oft < OFT__N ? oft_info[oft].ext1 : 0;
1152 GenDestFileName(f,dest,default_name,ext,true);
1153 }
1154
1155 ///////////////////////////////////////////////////////////////////////////////
1156
1157 const OFT_info_t oft_info[OFT__N+1] =
1158 {
1159 // HINT: The extra 0 in "\0" is needed because of: oft_info[].ext1+1
1160
1161 // { oft, attrib, iom,
1162 // name, option, ext1, ext2, info },
1163
1164 { OFT_UNKNOWN,
1165 0,
1166 IOM__IS_DEFAULT,
1167 "?", 0, "\0", 0, "unkown file format" },
1168
1169 { OFT_PLAIN,
1170 OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY|OFT_A_LOADER,
1171 IOM_IS_IMAGE,
1172 "ISO", "--iso", ".iso", 0, "plain file" },
1173
1174 { OFT_WDF,
1175 OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY,
1176 IOM_IS_IMAGE,
1177 "WDF", "--wdf", ".wdf", 0, "Wii Disc Format" },
1178
1179 { OFT_CISO,
1180 OFT_A_READ|OFT_A_WRITE|OFT_A_MODIFY|OFT_A_NOSIZE|OFT_A_LOADER,
1181 IOM_IS_IMAGE,
1182 "CISO", "--ciso", ".ciso", ".wbi", "Compact ISO" },
1183
1184 { OFT_WBFS,
1185 OFT_A_READ|OFT_A_WRITE|OFT_A_EXTEND|OFT_A_MODIFY|OFT_A_NOSIZE|OFT_A_LOADER,
1186 IOM_IS_IMAGE,
1187 "WBFS", "--wbfs", ".wbfs", 0, "Wii Backup File System" },
1188
1189 { OFT_WIA,
1190 OFT_A_READ|OFT_A_WRITE|OFT_A_COMPR,
1191 IOM_IS_WIA,
1192 "WIA", "--wia", ".wia", 0, "compressed Wii ISO Archive" },
1193
1194 { OFT_FST,
1195 OFT_A_READ|OFT_A_WRITE|OFT_A_FST,
1196 IOM__IS_DEFAULT,
1197 "FST", "--fst", "\0", 0, "extracted File System" },
1198
1199 { OFT__N,
1200 0,
1201 IOM__IS_DEFAULT,
1202 0, 0, 0, 0, 0 },
1203 };
1204
1205 ///////////////////////////////////////////////////////////////////////////////
1206
1207 const CommandTab_t ImageTypeTab[] =
1208 {
1209 { OFT_PLAIN, "ISO", "PLAIN", 0 },
1210 { OFT_WDF, "WDF", 0, 0 },
1211 { OFT_CISO, "CISO", 0, 0 },
1212 { OFT_WBFS, "WBFS", 0, 0 },
1213 { OFT_WIA, "ISO", 0, 0 },
1214 { OFT_FST, "FST", 0, 1 },
1215 { 0,0,0,0 }
1216 };
1217
1218 ///////////////////////////////////////////////////////////////////////////////
1219
CalcOFT(enumOFT force,ccp fname_dest,ccp fname_src,enumOFT def)1220 enumOFT CalcOFT ( enumOFT force, ccp fname_dest, ccp fname_src, enumOFT def )
1221 {
1222 if ( force > OFT_UNKNOWN && force < OFT__N )
1223 return force;
1224
1225 ccp fname = IsDirectory(fname_dest,true) ? fname_src : fname_dest;
1226 if (fname)
1227 {
1228 const size_t len = strlen(fname);
1229 if ( len >= 4 )
1230 {
1231 if ( !strcasecmp(fname+len-4,".wdf") )
1232 return OFT_WDF;
1233
1234 if ( !strcasecmp(fname+len-4,".wia") )
1235 return OFT_WIA;
1236
1237 if ( !strcasecmp(fname+len-4,".iso") )
1238 return OFT_PLAIN;
1239
1240 if ( !strcasecmp(fname+len-4,".wbi") )
1241 return OFT_CISO;
1242
1243 if ( len >= 5 )
1244 {
1245 if ( !strcasecmp(fname+len-5,".wbfs") )
1246 return OFT_WBFS;
1247
1248 if ( !strcasecmp(fname+len-5,".ciso") )
1249 return OFT_CISO;
1250 }
1251 }
1252 }
1253
1254 return def;
1255 }
1256
1257 //
1258 ///////////////////////////////////////////////////////////////////////////////
1259 /////////////// split file support ///////////////
1260 ///////////////////////////////////////////////////////////////////////////////
1261
ExtractSplitMap(File_t * f,File_t * f2)1262 static void ExtractSplitMap
1263 (
1264 File_t * f, // main file
1265 File_t * f2 // split file
1266 )
1267 {
1268 DASSERT(f);
1269 DASSERT(f2);
1270
1271 if ( !f2->prealloc_done && f->prealloc_map.used )
1272 {
1273 const off_t split_end = f2->split_off + f2->split_filesize;
1274 MemMapItem_t ** end_field = f->prealloc_map.field + f->prealloc_map.used;
1275 MemMapItem_t **ptr;
1276 for ( ptr = f->prealloc_map.field; ptr < end_field; ptr++ )
1277 {
1278 off_t beg = (*ptr)->off;
1279 off_t end = beg + (*ptr)->size;
1280 if ( beg < split_end && end > f2->split_off )
1281 {
1282 if ( beg < f2->split_off )
1283 beg = f2->split_off;
1284 if ( end > split_end )
1285 end = split_end;
1286 PRINT(">>> PREALLOC: fd=%u, %9llx .. %9llx\n",f2->fd,(u64)beg,(u64)end);
1287 MemMapItem_t * item
1288 = InsertMemMapTie(&f2->prealloc_map,beg-f2->split_off,end-beg);
1289 DASSERT(item);
1290 snprintf(item->info,sizeof(item->info),
1291 "v-off=%llx, %s\n",
1292 (u64)beg, wd_print_size(0,0,end-beg,false,WD_SIZE_AUTO) );
1293 }
1294 }
1295 }
1296 }
1297
1298 ///////////////////////////////////////////////////////////////////////////////
1299
XSetupSplitFile(XPARM File_t * f,enumOFT oft,off_t split_size)1300 enumError XSetupSplitFile ( XPARM File_t *f, enumOFT oft, off_t split_size )
1301 {
1302 ASSERT(f);
1303 if (f->split_f)
1304 return ERR_OK;
1305
1306 if ( f->fd == -1 || !S_ISREG(f->st.st_mode) )
1307 {
1308 if (!f->disable_errors)
1309 ERROR0(ERR_WARNING,
1310 "Split file support not possible: %s\n", f->fname );
1311 return ERR_WARNING;
1312 }
1313
1314 // at very first: setup split file format
1315
1316 FreeString(f->split_fname_format);
1317 FreeString(f->split_rename_format);
1318 if (f->rename)
1319 {
1320 f->split_fname_format = AllocSplitFilename(f->fname,OFT_PLAIN);
1321 f->split_rename_format = AllocSplitFilename(f->rename,oft);
1322 }
1323 else
1324 f->split_fname_format = AllocSplitFilename(f->fname,oft);
1325
1326 char fname[PATH_MAX];
1327
1328 if (!f->is_writing)
1329 {
1330 // for read only files: test if split files available
1331 snprintf(fname,sizeof(fname),f->split_fname_format,1);
1332 struct stat st;
1333 if (stat(fname,&st))
1334 {
1335 bool found = false;
1336 const int slen = strlen(f->split_fname_format);
1337 if ( slen > 2 && f->split_fname_format[slen-2] == '1' )
1338 {
1339 int fw;
1340 for ( fw = '2'; fw <= '6'; fw++ )
1341 {
1342 ((char*)f->split_fname_format)[slen-2] = fw;
1343 snprintf(fname,sizeof(fname),f->split_fname_format,1);
1344 if (!stat(fname,&st))
1345 {
1346 found = true;
1347 break;
1348 }
1349 }
1350 }
1351 if (!found)
1352 return ERR_OK; // no split files found -> return
1353 }
1354 noPRINT("SPLIT-FNAME: %s\n",f->split_fname_format);
1355 }
1356
1357 File_t ** list = CALLOC(MAX_SPLIT_FILES,sizeof(*list));
1358 File_t * first;
1359 *list = first = MALLOC(sizeof(File_t));
1360
1361 // copy all but cache
1362 memcpy(first,f,sizeof(File_t));
1363
1364 first->is_caching = false;
1365 first->cache = 0;
1366 first->cur_cache = 0;
1367
1368 const bool have_stream = f->fp != 0;
1369 f->fp = 0;
1370 f->fd = -1;
1371 f->split_f = list;
1372 f->split_used = 1;
1373
1374 if (f->rename)
1375 {
1376 // fname is only informative -> use the final name
1377 f->fname = STRDUP(f->rename);
1378 f->rename = 0;
1379 }
1380 else
1381 f->fname = STRDUP(f->fname);
1382 if (f->path)
1383 f->path = STRDUP(f->path);
1384
1385 if ( oft == OFT_PLAIN || oft == OFT_CISO || oft == OFT_WBFS )
1386 {
1387 f->split_filesize = split_size ? split_size : DEF_SPLIT_SIZE_ISO;
1388 if ( DEF_SPLIT_FACTOR_ISO > 0 )
1389 {
1390 f->split_filesize &= ~(DEF_SPLIT_FACTOR_ISO-1);
1391 if (!f->split_filesize)
1392 f->split_filesize = DEF_SPLIT_FACTOR_ISO;
1393 }
1394 }
1395 else
1396 {
1397 f->split_filesize = split_size ? split_size : DEF_SPLIT_SIZE;
1398 if ( DEF_SPLIT_FACTOR > 0 )
1399 {
1400 f->split_filesize &= ~(DEF_SPLIT_FACTOR-1);
1401 if (!f->split_filesize)
1402 f->split_filesize = DEF_SPLIT_FACTOR;
1403 }
1404 }
1405
1406 first->split_filesize = first->st.st_size;
1407 first->split_fname_format = EmptyString;
1408 first->split_rename_format = 0;
1409 first->outname = 0;
1410
1411 PRINT("#S# Split setup, size=%llu, fname=%s\n",
1412 (u64)f->split_filesize, f->fname );
1413 PRINT("#S#0# split setup, size=%llu, fname=%s\n",
1414 (u64)first->split_filesize, first->fname );
1415
1416 noTRACE(" fname: %p %p\n",f->fname,first->fname);
1417 noTRACE(" path: %p %p\n",f->path,first->path);
1418 noTRACE(" rename: %p %p\n",f->rename,first->rename);
1419 noTRACE(" outname: %p %p\n",f->outname,first->outname);
1420 noTRACE(" split-fname-format: %s\n",f->split_fname_format);
1421 noTRACE(" split-rename-format: %s\n",f->split_rename_format);
1422
1423 ExtractSplitMap(f,first);
1424
1425 if (f->is_reading)
1426 {
1427 int idx;
1428 for ( idx = 1; idx < MAX_SPLIT_FILES; idx++ )
1429 {
1430 snprintf(fname,sizeof(fname),f->split_fname_format,idx);
1431 struct stat st;
1432 if (stat(fname,&st))
1433 break;
1434
1435 File_t * fi = MALLOC(sizeof(*f));
1436 InitializeFile(fi);
1437 fi->open_flags = f->active_open_flags;
1438 fi->fname = STRDUP(fname);
1439 enumError err = XOpenFileHelper( XCALL fi,
1440 have_stream ? IOM_FORCE_STREAM : IOM_NO_STREAM,
1441 f->active_open_flags, f->active_open_flags );
1442 if (err)
1443 {
1444 CloseFile(fi,false);
1445 FREE(fi);
1446 return err;
1447 }
1448
1449 if (f->split_rename_format)
1450 {
1451 ASSERT(!fi->rename);
1452 snprintf(fname,sizeof(fname),f->split_rename_format,idx);
1453 fi->rename = STRDUP(fname);
1454 }
1455
1456 fi->split_off = f->split_f[idx-1]->split_off
1457 + f->split_f[idx-1]->split_filesize;
1458 fi->split_filesize = fi->st.st_size;
1459
1460 PRINT("#S#%u# Open %s, soff=%llx, ssize=%llx\n",
1461 idx, fname, (u64)fi->split_off, (u64)fi->split_filesize );
1462 f->st.st_size += fi->st.st_size;
1463 f->split_f[idx] = fi;
1464 }
1465 f->split_used = idx;
1466 f->fatt.size = f->st.st_size;
1467 }
1468
1469 File_t * fi = f->split_f[f->split_used-1];
1470 ASSERT(fi);
1471 if ( fi->split_filesize < f->split_filesize )
1472 {
1473 TRACE("#S#%u# new ssize: %llx -> %llx\n",
1474 f->split_used-1, (u64)fi->split_filesize, (u64)f->split_filesize );
1475 fi->split_filesize = f->split_filesize;
1476 }
1477
1478 #ifdef DEBUG
1479 {
1480 int i;
1481 for ( i = 0; i < f->split_used; i++ )
1482 TRACE(" - %2d: ssize= %9llx\n",i,(u64)f->split_f[i]->split_filesize);
1483 }
1484 #endif
1485
1486 return ERR_OK;
1487 }
1488
1489 ///////////////////////////////////////////////////////////////////////////////
1490
XCreateSplitFile(XPARM File_t * f,uint split_idx)1491 enumError XCreateSplitFile ( XPARM File_t *f, uint split_idx )
1492 {
1493 ASSERT( f );
1494 ASSERT( f->split_f );
1495 ASSERT( split_idx > 0 );
1496 TRACE("#S# CreateSplitFile() %u/%u/%u\n",split_idx,f->split_used,MAX_SPLIT_FILES);
1497
1498 if ( split_idx > MAX_SPLIT_FILES )
1499 {
1500 if ( !f->disable_errors )
1501 PrintError( XERROR1, ERR_WRITE_FAILED,
1502 "Max number of split files (%d,off=%llx) reached: %s\n",
1503 MAX_SPLIT_FILES, (u64)f->file_off, f->fname );
1504
1505 f->last_error = ERR_WRITE_FAILED;
1506 if ( f->max_error < f->last_error )
1507 f->max_error = f->last_error;
1508 return ERR_WRITE_FAILED;
1509 }
1510
1511 while ( f->split_used <= split_idx )
1512 {
1513 File_t ** ptr = f->split_f + f->split_used;
1514 ASSERT(!*ptr);
1515
1516 File_t * prev;
1517 if ( f->split_used > 0 )
1518 {
1519 prev = ptr[-1];
1520 if ( prev->is_writing )
1521 SetSizeF(prev,prev->split_filesize);
1522 }
1523 else
1524 prev = 0;
1525
1526 char fname[PATH_MAX];
1527 snprintf(fname,sizeof(fname),f->split_fname_format,f->split_used++);
1528 TRACE("#S#%u# Create %s\n",f->split_used-1,fname);
1529
1530 File_t * f2 = MALLOC(sizeof(File_t));
1531 InitializeFile(f2);
1532 *ptr = f2;
1533 const int flags = O_CREAT|O_WRONLY|O_TRUNC|O_EXCL|f->active_open_flags;
1534 f2->fname = STRDUP(fname);
1535 enumError err
1536 = XOpenFileHelper( XCALL f2,
1537 prev && prev->fp ? IOM_FORCE_STREAM : IOM_NO_STREAM,
1538 flags, flags );
1539 if (err)
1540 return err;
1541 if (prev)
1542 f2->split_off = prev->split_off + prev->split_filesize;
1543 f2->split_filesize = f->split_filesize;
1544 PRINT("SPLIT #%u: %9llx + %9llx\n",
1545 split_idx, (u64)f2->split_off,(u64)f2->split_filesize);
1546 if (f->split_rename_format)
1547 {
1548 ASSERT(!f2->rename);
1549 snprintf(fname,sizeof(fname),f->split_rename_format,f->split_used-1);
1550 f2->rename = STRDUP(fname);
1551 }
1552
1553 ExtractSplitMap(f,f2);
1554 }
1555 return ERR_OK;
1556 }
1557
1558 ///////////////////////////////////////////////////////////////////////////////
1559
XFindSplitFile(XPARM File_t * f,uint * p_index,off_t * p_off)1560 enumError XFindSplitFile ( XPARM File_t *f, uint * p_index, off_t * p_off )
1561 {
1562 ASSERT( f );
1563 ASSERT( f->split_f );
1564 ASSERT(p_index);
1565 ASSERT(p_off);
1566 off_t off = *p_off;
1567 TRACE("#S# XFindSplitFile(off=%llx) %u/%u\n",(u64)off,f->split_used,MAX_SPLIT_FILES);
1568
1569 File_t ** ptr = f->split_f;
1570 for (;;)
1571 {
1572 if (!*ptr)
1573 {
1574 enumError err = XCreateSplitFile( XCALL f, ptr-f->split_f );
1575 if (err)
1576 return err;
1577 }
1578
1579 File_t *cur = *ptr;
1580 ASSERT(cur);
1581 if ( off <= cur->split_filesize )
1582 {
1583 *p_index = ptr - f->split_f;
1584 *p_off = off;
1585 return ERR_OK;
1586 }
1587
1588 // skip this file
1589 off -= cur->split_filesize;
1590 ptr++;
1591 }
1592 }
1593
1594 //
1595 ///////////////////////////////////////////////////////////////////////////////
1596 ///////////////////////////////////////////////////////////////////////////////
1597
ClearCache(File_t * f)1598 void ClearCache ( File_t * f )
1599 {
1600 DASSERT(f);
1601 f->is_caching = false;
1602 while (f->cache)
1603 {
1604 FileCache_t * ptr = f->cache;
1605 f->cache = ptr->next;
1606 FREE((char*)ptr->data);
1607 FREE(ptr);
1608 }
1609 f->cur_cache = 0;
1610 }
1611
1612 ///////////////////////////////////////////////////////////////////////////////
1613
DefineCachedArea(File_t * f,off_t off,size_t count)1614 void DefineCachedArea ( File_t * f, off_t off, size_t count )
1615 {
1616 // this whole function assumes that count is small and that the hole
1617 // cache size is less than MAX(size_t)
1618
1619 ASSERT(f);
1620
1621 f->cur_cache = 0;
1622 f->is_caching = f->fd != -1 && f->is_reading && !f->is_writing;
1623 TRACE("DefineCachedArea(,%llx,%zx) fd=%d rw=%d,%d is_caching=%d, filesize=%llx\n",
1624 (u64)off, count, f->fd, f->is_reading, f->is_writing, f->is_caching,
1625 (u64)f->st.st_size );
1626 if (!f->is_caching)
1627 return;
1628
1629 const size_t blocksize = f->st.st_blksize > HD_SECTOR_SIZE
1630 ? f->st.st_blksize : HD_SECTOR_SIZE;
1631
1632 off_t off_end = (( off + count + blocksize - 1 ) / blocksize ) * blocksize;
1633 off = ( off / blocksize ) * blocksize;
1634 count = (size_t)( off_end - off );
1635
1636 if (f->st.st_size)
1637 {
1638 // limit cache to file size if filesize is known
1639
1640 if ( off >= f->st.st_size )
1641 return;
1642
1643 if ( off + count > f->st.st_size )
1644 count = f->st.st_size - off;
1645 }
1646
1647 FileCache_t **pptr;
1648 for ( pptr = &f->cache; *pptr; pptr = &(*pptr)->next )
1649 {
1650 FileCache_t * ptr = *pptr;
1651 if ( off_end < ptr->off )
1652 break;
1653
1654 off_t ptr_end = ptr->off + ptr->count;
1655 if ( off < ptr_end || off == ptr_end && !ptr->data )
1656 {
1657 // extend this cache element
1658 if (ptr->data)
1659 {
1660 // already cached -> try a smaller area
1661 if ( off_end > ptr_end )
1662 DefineCachedArea(f,ptr_end,off_end-ptr_end);
1663 return;
1664 }
1665
1666 off_t new_end = ptr->off + ptr->count;
1667 if ( new_end < off_end )
1668 new_end = off_end;
1669
1670 if ( ptr->off < off )
1671 ptr->off = off;
1672 ptr->count = new_end - ptr->off;
1673 TRACE("#F# CACHE ENTRY extended: o=%10llx n=%9zx\n",(u64)ptr->off,ptr->count);
1674 return;
1675 }
1676 }
1677
1678 // insert a new cache element
1679 FileCache_t * ptr = CALLOC(1,sizeof(FileCache_t));
1680 ptr->off = off;
1681 ptr->count = count;
1682 ptr->next = *pptr;
1683 *pptr = ptr;
1684 TRACE("#F# CACHE ENTRY inserted: o=%10llx n=%9zx\n",(u64)ptr->off,ptr->count);
1685 }
1686
1687 //-----------------------------------------------------------------------------
1688
DefineCachedAreaISO(File_t * f,bool head_only)1689 void DefineCachedAreaISO ( File_t * f, bool head_only )
1690 {
1691 ASSERT(f);
1692 if (head_only)
1693 {
1694 DefineCachedArea(f,0,CISO_HEAD_SIZE+FILE_PRELOAD_SIZE);
1695 }
1696 else
1697 {
1698 DefineCachedArea(f,0x0000000ull,0x200000);
1699 DefineCachedArea(f,0xf800000ull,0x800000);
1700 }
1701 }
1702
1703 ///////////////////////////////////////////////////////////////////////////////
1704 ///////////////////////////////////////////////////////////////////////////////
1705
XAnalyzeWH(XPARM File_t * f,WDF_Head_t * wh,bool print_err)1706 enumError XAnalyzeWH ( XPARM File_t * f, WDF_Head_t * wh, bool print_err )
1707 {
1708 ASSERT(wh);
1709 TRACE("AnalyzeWH()\n");
1710
1711 if (memcmp(wh->magic,WDF_MAGIC,WDF_MAGIC_SIZE))
1712 {
1713 TRACE(" - magic failed\n");
1714 return ERR_NO_WDF;
1715 }
1716
1717 if (!f->seek_allowed)
1718 {
1719 TRACE(" - seek not allowed\n");
1720 return print_err
1721 ? PrintError( XERROR0, ERR_WRONG_FILE_TYPE,
1722 "Wrong file type: %s\n", f->fname )
1723 : ERR_WRONG_FILE_TYPE;
1724 }
1725
1726 const size_t wdf_head_size = AdjustHeaderWDF(wh);
1727
1728 #if WDF2_ENABLED
1729 if ( !wh->wdf_version || wh->wdf_compatible > WDF_VERSION )
1730 #else
1731 if ( !wh->wdf_version || wh->wdf_version > WDF_VERSION )
1732 #endif
1733 {
1734 #if WDF2_ENABLED
1735 PRINT(" - wrong WDF version: %u,%u\n",wh->wdf_version,wh->wdf_compatible);
1736 #else
1737 PRINT(" - wrong WDF version: %u\n",wh->wdf_version);
1738 #endif
1739 return print_err
1740 ? PrintError( XERROR0, ERR_WDF_VERSION,
1741 #if WDF2_ENABLED
1742 "Only WDF version 1..%u supported but not version %u.\n",
1743 #else
1744 "Only WDF version %u supported but not version %u.\n",
1745 #endif
1746 WDF_VERSION, wh->wdf_version )
1747 : ERR_WDF_VERSION;
1748 }
1749
1750 const u32 chunk_tab_size = wh->chunk_n * sizeof(WDF_Chunk_t);
1751 if ( f->st.st_size < wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size )
1752 {
1753 // file size to short -> maybe a splitted file
1754 XSetupSplitFile(XCALL f,OFT_UNKNOWN,0);
1755 }
1756
1757 if ( wh->chunk_off != wh->data_size + wdf_head_size
1758 || wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size != f->st.st_size )
1759 {
1760 PRINT(" - file size error\n");
1761 PRINT(" - %llx ? %llx = %llx + %zx\n",
1762 (u64)wh->chunk_off, wh->data_size + wdf_head_size,
1763 wh->data_size, wdf_head_size );
1764 PRINT(" - %llx + %x + %x = %llx ? %llx\n",
1765 (u64)wh->chunk_off, WDF_MAGIC_SIZE, chunk_tab_size,
1766 (u64)wh->chunk_off + WDF_MAGIC_SIZE + chunk_tab_size,
1767 (u64)f->st.st_size );
1768
1769 return print_err
1770 ? PrintError( XERROR0, ERR_WDF_INVALID, "Invalid WDF file: %s\n",f->fname )
1771 : ERR_WDF_INVALID;
1772 }
1773
1774 TRACE(" - OK\n");
1775 return ERR_OK;
1776 }
1777
1778 //
1779 ///////////////////////////////////////////////////////////////////////////////
1780 ///////////////////////////////////////////////////////////////////////////////
1781
XCacheHelper(XPARM File_t * f,off_t off,size_t count)1782 static FileCache_t * XCacheHelper ( XPARM File_t * f, off_t off, size_t count )
1783 {
1784 // This function does the following:
1785 // a) look into the cache table to find if (off,count) is part of an
1786 // cache entry. I so, the result is a pointer to the first affected
1787 // cache entry.
1788 // b) All cache entires up to off are filled with data. If 'off' is part
1789 // of an entry this entry is filled completly.
1790 // c) If the result is not NULL a seek or a seek simulation is done.
1791
1792 ASSERT(f);
1793 f->last_error = ERR_OK;
1794 if (!f->is_caching)
1795 return 0;
1796
1797 // cur_cache is always pointing to the last used and loaded cache entry
1798 ASSERT( !f->cur_cache || f->cur_cache->data );
1799
1800 FileCache_t * cptr = f->cur_cache;
1801 if ( !cptr || off < cptr->off )
1802 cptr = f->cache;
1803
1804 f->is_caching = false; // disable cache access for sub calls
1805 while (cptr)
1806 {
1807 TRACE(" - SEARCH=%llx..%llx CACHE=%llx..%llx\n",
1808 (u64)off, (u64)off + count, (u64)cptr->off, (u64)cptr->off + cptr->count );
1809
1810 if ( off + count <= cptr->off )
1811 {
1812 cptr = 0;
1813 break;
1814 }
1815
1816 if (!cptr->data)
1817 {
1818 // cache must be filled
1819 char * data = MALLOC(cptr->count);
1820 cptr->data = data;
1821 TRACE(TRACE_RDWR_FORMAT, "#F# FILL CACHE",
1822 GetFD(f), GetFP(f), (u64)cptr->off,
1823 (u64)cptr->off+cptr->count, cptr->count, "" );
1824 const enumError stat = XReadAtF(XCALL f,cptr->off,data,cptr->count);
1825 if (stat)
1826 {
1827 cptr = 0;
1828 break;
1829 }
1830 }
1831 if ( off < cptr->off + cptr->count )
1832 break;
1833 cptr = cptr->next;
1834 }
1835 f->is_caching = true; // restore cache access
1836
1837 if (cptr)
1838 {
1839 // cache entry found!
1840 ASSERT( off + count > cptr->off );
1841 ASSERT( off < cptr->off + cptr->count );
1842
1843 f->cur_off = off;
1844 f->cur_cache = cptr; // save the current ptr
1845 ASSERT(cptr->data);
1846
1847 TRACE(TRACE_RDWR_FORMAT, "#F# CACHE FOUND",
1848 GetFD(f), GetFP(f), (u64)cptr->off,
1849 (u64)cptr->off+cptr->count, cptr->count, "" );
1850 }
1851
1852 return cptr;
1853 }
1854
1855 ///////////////////////////////////////////////////////////////////////////////
1856
PreallocHelper(File_t * f)1857 static void PreallocHelper ( File_t *f )
1858 {
1859 DASSERT(f);
1860
1861 #ifndef NO_PREALLOC
1862 if ( !f->prealloc_done && f->prealloc_map.used )
1863 {
1864 f->prealloc_done = true;
1865 if (f->split_f)
1866 {
1867 ExtractSplitMap(f,f->split_f[0]);
1868 PreallocHelper(f->split_f[0]);
1869 }
1870 else
1871 {
1872 if ( logging > 0 )
1873 {
1874 printf("\n Preallocation table:\n");
1875 PrintMemMap(&f->prealloc_map,stdout,3,0);
1876 putchar('\n');
1877 }
1878
1879 // prealloc largest block first
1880
1881 MemMapItem_t ** end_field = f->prealloc_map.field + f->prealloc_map.used;
1882 for(;;)
1883 {
1884 off_t found_size = 0;
1885 MemMapItem_t **ptr, *found = 0;
1886 for ( ptr = f->prealloc_map.field; ptr < end_field; ptr++ )
1887 if ( (*ptr)->size > found_size )
1888 {
1889 found = *ptr;
1890 found_size = found->size;
1891 }
1892 if (!found)
1893 break;
1894
1895 #ifdef HAVE_FALLOCATE
1896 noPRINT("CALL fallocate(%d,0,%9llx,%9llx [%s])\n",
1897 f->fd, (u64)found->off, (u64)found->size,
1898 wd_print_size_1024(0,0,found->size,true) );
1899 fallocate(f->fd,0,found->off,found->size);
1900 noPRINT("TERM fallocate()\n");
1901 #elif HAVE_POSIX_FALLOCATE
1902 noPRINT("CALL posix_fallocate(%d,%9llx,%9llx [%s])\n",
1903 f->fd, (u64)found->off, (u64)found->size,
1904 wd_print_size_1024(0,0,found->size,true) );
1905 posix_fallocate(f->fd,found->off,found->size);
1906 noPRINT("TERM posix_fallocate()\n");
1907 #elif __APPLE__
1908 noPRINT("CALL fcntl(%d,F_PREALLOCATE,%9llx,%9llx [%s])\n",
1909 f->fd, (u64)found->off, (u64)found->size,
1910 wd_print_size_1024(0,0,found->size,true) );
1911 fstore_t fst;
1912 fst.fst_flags = F_ALLOCATECONTIG;
1913 fst.fst_posmode = F_PEOFPOSMODE;
1914 fst.fst_offset = found->off;
1915 fst.fst_length = found->size;
1916 fst.fst_bytesalloc = 0;
1917 fcntl( f->fd, F_PREALLOCATE, &fst );
1918 noPRINT("TERM fcntl()\n");
1919 #else
1920 #error "no preallocation support -> use -DNO_PREALLOC"
1921 #endif
1922
1923 found->size = 0;
1924 }
1925 }
1926 }
1927 #endif
1928 }
1929
1930 ///////////////////////////////////////////////////////////////////////////////
1931
XTellF(XPARM File_t * f)1932 enumError XTellF ( XPARM File_t * f )
1933 {
1934 ASSERT(f);
1935
1936 if (f->split_f)
1937 {
1938 // file pos is pure virtual
1939 return f->cur_off;
1940 }
1941
1942 f->file_off = f->fp
1943 ? ftello(f->fp)
1944 : f->fd != -1
1945 ? lseek(f->fd,(off_t)0,SEEK_CUR)
1946 : (off_t)-1;
1947
1948 if ( f->file_off == (off_t)-1 )
1949 {
1950 f->last_error = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
1951 if ( f->max_error < f->last_error )
1952 f->max_error = f->last_error;
1953 if (!f->disable_errors)
1954 PrintError( XERROR1, f->last_error, "Tell failed [%c=%d]: %s\n",
1955 GetFT(f), GetFD(f), f->fname );
1956 }
1957 else
1958 {
1959 f->last_error = ERR_OK;
1960 f->tell_count++;
1961 if ( f->max_off < f->file_off )
1962 f->max_off = f->file_off;
1963 }
1964
1965 TRACE(TRACE_SEEK_FORMAT, "#F# TellF()", GetFD(f), GetFP(f), (u64)f->file_off, "" );
1966 f->cur_off = f->file_off;
1967 return f->last_error;
1968 }
1969
1970 ///////////////////////////////////////////////////////////////////////////////
1971
XSeekF(XPARM File_t * f,off_t off)1972 enumError XSeekF ( XPARM File_t * f, off_t off )
1973 {
1974 ASSERT(f);
1975
1976 // sync virtual off
1977 f->cur_off = f->file_off;
1978
1979 // if we are already at off and pure reading or pure writing -> all done
1980 if ( off == f->file_off && f->is_writing != f->is_reading )
1981 return f->last_error = ERR_OK;
1982
1983 if (f->is_caching)
1984 {
1985 FileCache_t * cptr = XCacheHelper(XCALL f,off,1);
1986 if (cptr)
1987 return f->last_error; // all done
1988 }
1989
1990 if (!f->prealloc_done)
1991 PreallocHelper(f);
1992
1993 if (f->split_f)
1994 {
1995 // file pos is pure virtual
1996 f->cur_off = off;
1997 return ERR_OK;
1998 }
1999
2000 if (!f->seek_allowed)
2001 {
2002 if ( !f->is_reading && off >= f->file_off )
2003 {
2004 // simulate seek with 'write zero' operations
2005
2006 off_t skip_size = off - f->file_off;
2007 TRACE(TRACE_RDWR_FORMAT, "#F# SEEK SIMULATION",
2008 GetFD(f), GetFP(f), (u64)f->file_off,
2009 (u64)f->file_off+skip_size, (size_t)skip_size, "" );
2010
2011 while (skip_size)
2012 {
2013 if (SIGINT_level>1)
2014 return ERR_INTERRUPT;
2015
2016 const size_t max_write = skip_size < sizeof(zerobuf)
2017 ? (size_t)skip_size : sizeof(zerobuf);
2018 const enumError stat = XWriteF(XCALL f,zerobuf,max_write);
2019 if (stat)
2020 return stat;
2021 skip_size -= max_write;
2022 noTRACE("R+W-Count: %llu + %llu\n",f->bytes_read,f->bytes_written);
2023 }
2024 ASSERT( SIGINT_level || off == f->file_off );
2025 ASSERT( SIGINT_level || off == f->cur_off );
2026 return ERR_OK;
2027 }
2028
2029 if ( !f->is_writing && off >= f->file_off )
2030 {
2031 // simulate seek with read operations
2032
2033 off_t skip_size = off - f->file_off;
2034 TRACE(TRACE_RDWR_FORMAT, "#F# SEEK SIMULATION",
2035 GetFD(f), GetFP(f), (u64)f->file_off,
2036 (u64)f->file_off+skip_size, (size_t)skip_size, "" );
2037
2038 while (skip_size)
2039 {
2040 if (SIGINT_level>1)
2041 return ERR_INTERRUPT;
2042
2043 const size_t max_read = skip_size < sizeof(iobuf)
2044 ? (size_t)skip_size : sizeof(iobuf);
2045 const enumError stat = XReadF(XCALL f,iobuf,max_read);
2046 if (stat)
2047 return stat;
2048 skip_size -= max_read;
2049 noTRACE("R+W-Count: %llu + %llu\n",f->bytes_read,f->bytes_written);
2050 }
2051 ASSERT( SIGINT_level || off == f->file_off );
2052 ASSERT( SIGINT_level || off == f->cur_off );
2053 return ERR_OK;
2054 }
2055
2056 if ( f->is_caching )
2057 {
2058 char buf[100];
2059 snprintf(buf,sizeof(buf),"ID=%s, OFF=%llx, SIZE=%x",
2060 f->id6_src, (u64)f->cache_info_off, (u32)f->cache_info_size );
2061 fprintf(stderr,
2062 "\n"
2063 "************************************************************************\n"
2064 "***** It seems, that the caching area for the game is too small! *****\n"
2065 "***** Please report this to the author. *****\n"
2066 "***** Technical data: %-43s *****\n"
2067 "************************************************************************\n"
2068 "\n", buf );
2069 }
2070 }
2071
2072 TRACE(TRACE_SEEK_FORMAT, "#F# SeekF()",
2073 GetFD(f), GetFP(f), (u64)off, off < f->max_off ? " <" : "" );
2074
2075 const bool failed = f->fp
2076 ? fseeko(f->fp,off,SEEK_SET) == (off_t)-1
2077 : f->fd == -1 || lseek(f->fd,off,SEEK_SET) == (off_t)-1;
2078
2079 enumError err;
2080 if (failed)
2081 {
2082 err = f->is_writing ? ERR_WRITE_FAILED : ERR_READ_FAILED;
2083 f->last_error = err;
2084 if ( f->max_error < f->last_error )
2085 f->max_error = f->last_error;
2086 if (!f->disable_errors)
2087 PrintError( XERROR1, f->last_error,
2088 "Seek failed [%c=%d,%llu]: %s\n",
2089 GetFT(f), GetFD(f), (u64)off, f->fname );
2090 f->file_off = (off_t)-1;
2091 }
2092 else
2093 {
2094 err = ERR_OK;
2095 f->seek_count++;
2096 if ( f->max_off < f->file_off )
2097 f->max_off = f->file_off;
2098 }
2099
2100 f->cur_off = f->file_off = off;
2101 return err;
2102 }
2103
2104 ///////////////////////////////////////////////////////////////////////////////
2105
XSetSizeF(XPARM File_t * f,off_t size)2106 enumError XSetSizeF ( XPARM File_t * f, off_t size )
2107 {
2108 ASSERT(f);
2109 TRACE(TRACE_SEEK_FORMAT, "#F# SetSizeF()",
2110 GetFD(f), GetFP(f), (u64)size,
2111 size < f->max_off ? " <" : size > f->max_off ? " >" : "" );
2112
2113 if (!f->prealloc_done)
2114 PreallocHelper(f);
2115
2116 if (f->split_f)
2117 {
2118 f->max_off = size;
2119
2120 uint index;
2121 enumError err = XFindSplitFile( XCALL f, &index, &size );
2122 if (err)
2123 return err;
2124 ASSERT( index < MAX_SPLIT_FILES );
2125 File_t ** ptr = f->split_f + index;
2126 XSetSizeF(XCALL *ptr,size);
2127
2128 int count = f->split_used - index;
2129 f->split_used = index+1;
2130 while ( count-- > 1 )
2131 {
2132 ptr++;
2133 ASSERT(*ptr);
2134 XCloseFile( XCALL *ptr, true );
2135 FREE(*ptr);
2136 *ptr = 0;
2137 }
2138 return ERR_OK;
2139 }
2140
2141 //--------------------------------------------------
2142
2143 if (f->fp)
2144 fflush(f->fp); // [[2do]] ? error handling
2145
2146 if ( !f->seek_allowed && f->cur_off <= size )
2147 {
2148 if (!XSeekF(XCALL f,size))
2149 return f->last_error;
2150 }
2151 else if (ftruncate(f->fd,size))
2152 {
2153 f->last_error = ERR_WRITE_FAILED;
2154 if ( f->max_error < f->last_error )
2155 f->max_error = f->last_error;
2156 if (!f->disable_errors)
2157 PrintError( XERROR1, f->last_error,
2158 "Set file size failed [%c=%d,%llu]: %s\n",
2159 GetFT(f), GetFD(f), (u64)size, f->fname );
2160 return f->last_error;
2161 }
2162
2163 PRINT("fd=%d truncated to %llx\n",f->fd,(u64)size);
2164 f->setsize_count++;
2165 f->max_off = size;
2166 return ERR_OK;
2167 }
2168
2169 ///////////////////////////////////////////////////////////////////////////////
2170
XPreallocateF(XPARM File_t * f,off_t off,off_t size)2171 enumError XPreallocateF
2172 (
2173 XPARM // debugging and tracing infos
2174 File_t * f, // valid file, writing
2175 off_t off, // offset
2176 off_t size // needed size
2177 )
2178 {
2179 DASSERT(f);
2180 enumError err = ERR_OK;
2181 if ( size && prealloc_mode > PREALLOC_OFF )
2182 {
2183 noPRINT("PREALLOC/FILE fd=%d, %llx+%llx max=%llx: %s\n",
2184 f->fd, (u64)off, (u64)size, (u64)f->max_off, f->fname );
2185
2186 const off_t max = off + size;
2187 if ( f->prealloc_size < max )
2188 f->prealloc_size = max;
2189
2190 InsertMemMapTie(&f->prealloc_map,off,size);
2191 }
2192 return err;
2193 }
2194
2195 ///////////////////////////////////////////////////////////////////////////////
2196
XReadF(XPARM File_t * f,void * iobuf,size_t count)2197 enumError XReadF ( XPARM File_t * f, void * iobuf, size_t count )
2198 {
2199 ASSERT(f);
2200
2201 if (f->is_caching)
2202 {
2203 TRACE("#F# --- cache\n");
2204 TRACE(TRACE_RDWR_FORMAT, "#F# ReadF()+C",
2205 GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2206 f->cur_off < f->max_off ? " <" : "" );
2207
2208 off_t my_off = f->cur_off;
2209 while (count)
2210 {
2211 FileCache_t * cptr = XCacheHelper(XCALL f,my_off,count);
2212 if (f->last_error)
2213 return f->last_error;
2214 if (!cptr)
2215 break;
2216
2217 // there is an overlap
2218 TRACE(" my_off=%llx, count=%zx, cptr->off=%llx, cptr->count=%zx\n",
2219 (u64)my_off, count, (u64)cptr->off, cptr->count );
2220 //ASSERT( my_off + count > cptr->off );
2221 ASSERT( cptr->off + cptr->count > my_off );
2222
2223 if ( cptr->off > my_off )
2224 {
2225 // read data from file
2226
2227 TRACELINE;
2228 const size_t size = cptr->off - my_off;
2229 f->is_caching = false; // disable cache operations
2230 const enumError stat = XReadAtF(XCALL f,my_off,iobuf,size);
2231 f->is_caching = true; // restore cache operations
2232 if (stat)
2233 return stat;
2234 iobuf = (void*)( (char*)iobuf + size );
2235 count -= size;
2236 my_off += size;
2237 }
2238
2239 if ( count > 0 )
2240 {
2241 TRACELINE;
2242 ASSERT( my_off >= cptr->off );
2243 ASSERT( my_off < cptr->off + cptr->count );
2244 ASSERT( cptr->data );
2245 const u32 delta = my_off - cptr->off;
2246 ASSERT ( delta < cptr->count );
2247 size_t size = cptr->count - delta;
2248 if ( size > count )
2249 size = count;
2250 TRACE(TRACE_RDWR_FORMAT, "#F# COPY FROM CACHE",
2251 GetFD(f), GetFP(f), (u64)my_off, (u64)my_off+size, size, "" );
2252 memcpy(iobuf,cptr->data+delta,size);
2253 iobuf = (void*)( (char*)iobuf + size );
2254 count -= size;
2255 my_off += size;
2256 f->bytes_read += size;
2257 }
2258
2259 f->cur_off = my_off;
2260 if (!count)
2261 return ERR_OK;
2262 }
2263 }
2264
2265 //--------------------------------------------------
2266
2267 if (f->split_f)
2268 {
2269 TRACE("#S# ---\n");
2270 TRACE(TRACE_RDWR_FORMAT, "#S# ReadF()",
2271 GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2272 f->cur_off < f->max_off ? " <" : "" );
2273
2274 File_t ** ptr = f->split_f;
2275 off_t off = f->cur_off;
2276 f->cur_off = 0;
2277
2278 while ( count > 0 )
2279 {
2280 if (!*ptr)
2281 {
2282 ptr--;
2283 f->cur_off += off + count;
2284 f->bytes_read += count;
2285 return XReadAtF( XCALL *ptr, off, iobuf, count );
2286 }
2287
2288 File_t *cur = *ptr;
2289 ASSERT(cur);
2290 TRACE("#S#%zd# off=%llx cur_of=%llx count=%zx fsize=%llx\n",
2291 ptr-f->split_f, (u64)off, (u64)f->cur_off, count, (u64)cur->split_filesize );
2292
2293 if ( off < cur->split_filesize )
2294 {
2295 // read from this file
2296 const off_t max_count = cur->split_filesize - off;
2297 const size_t stream_count = count < max_count ? count : (size_t)max_count;
2298 enumError err = XReadAtF( XCALL cur, off, iobuf, stream_count );
2299 if (err)
2300 return err;
2301 f->bytes_read += stream_count;
2302 count -= stream_count;
2303 iobuf = (char*)iobuf + stream_count;
2304 f->cur_off += off + stream_count;
2305 off = 0;
2306 }
2307 else
2308 {
2309 // skip this file
2310 f->cur_off += cur->split_filesize;
2311 off -= cur->split_filesize;
2312 }
2313 ptr++;
2314 }
2315 if ( f->max_off < f->cur_off )
2316 f->max_off = f->cur_off;
2317
2318 TRACE("#S#x# off=%9llx cur_of=%9llx count=%zx\n", (u64)off, (u64)f->cur_off, count );
2319 return ERR_OK;
2320 }
2321
2322 //--------------------------------------------------
2323
2324 if ( f->cur_off != f->file_off )
2325 {
2326 const int stat = XSeekF(XCALL f,f->cur_off);
2327 if (stat)
2328 return stat;
2329 // f->cur_off and f->file_off may differ because of cache access
2330 }
2331
2332 TRACE(TRACE_RDWR_FORMAT, "#F# ReadF()",
2333 GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2334 f->cur_off < f->max_off ? " <" : "" );
2335
2336 if ( f->read_behind_eof && ( f->st.st_size > 0 || f->is_writing ) )
2337 {
2338 const off_t max_read = f->st.st_size > f->file_off
2339 ? f->st.st_size - f->file_off
2340 : 0;
2341
2342 TRACE("read_behind_eof=%d, st_size=%llx, count=%zx, max_read=%llx\n",
2343 f->read_behind_eof, (u64)f->st.st_size, count, (u64)max_read );
2344
2345 if ( count > max_read )
2346 {
2347 if ( f->read_behind_eof == 1 )
2348 {
2349 f->read_behind_eof = 2;
2350 if ( !f->disable_errors )
2351 PrintError( XERROR0, ERR_WARNING,
2352 "Read behind eof -> zero filled [%c=%d,%llu+%zu]: %s\n",
2353 GetFT(f), GetFD(f),
2354 (u64)f->file_off, count, f->fname );
2355 }
2356 size_t fill_count = count - (size_t)max_read;
2357 count = (size_t)max_read;
2358 memset(iobuf+count,0,fill_count);
2359
2360 if (!count)
2361 return ERR_OK;
2362 }
2363 }
2364
2365 bool err;
2366 size_t read_count = 0;
2367 if ( f->fd == -1 )
2368 {
2369 err = true;
2370 errno = 0;
2371 }
2372 else if (!count)
2373 err = false;
2374 else if (f->fp)
2375 {
2376 read_count = fread(iobuf,1,count,f->fp);
2377 err = read_count < count && errno;
2378 iobuf = (void*)( (char*)iobuf + read_count );
2379 }
2380 else
2381 {
2382 err = false;
2383 size_t size = count;
2384 while (size)
2385 {
2386 ssize_t rstat = read(f->fd,iobuf,size);
2387 if ( rstat <= 0 )
2388 {
2389 err = rstat < 0;
2390 break;
2391 }
2392 read_count += rstat;
2393 size -= rstat;
2394 iobuf = (void*)( (char*)iobuf + rstat );
2395 }
2396 }
2397
2398 if ( err || read_count < count && !f->read_behind_eof )
2399 {
2400 if ( !f->disable_errors && f->last_error != ERR_READ_FAILED )
2401 PrintError( XERROR1, ERR_READ_FAILED,
2402 "Read failed [%c=%d,%llu+%zu]: %s\n",
2403 GetFT(f), GetFD(f),
2404 (u64)f->file_off, count, f->fname );
2405
2406 f->cur_off = f->file_off = (off_t)-1ll;
2407
2408 if ( f->max_error < ERR_READ_FAILED )
2409 f->max_error = ERR_READ_FAILED;
2410 return f->last_error = ERR_READ_FAILED;
2411 }
2412
2413 f->read_count++;
2414 f->bytes_read += read_count;
2415 f->file_off += read_count;
2416 f->cur_off = f->file_off;
2417 if ( f->max_off < f->file_off )
2418 f->max_off = f->file_off;
2419
2420 if ( read_count < count )
2421 {
2422 if (!f->st.st_size)
2423 f->st.st_size = f->file_off;
2424 f->cur_off = f->file_off;
2425 return XReadAtF(XCALL f,f->file_off,iobuf,count-read_count);
2426 }
2427
2428 return ERR_OK;
2429 }
2430
2431 ///////////////////////////////////////////////////////////////////////////////
2432
XWriteF(XPARM File_t * f,const void * iobuf,size_t count)2433 enumError XWriteF ( XPARM File_t * f, const void * iobuf, size_t count )
2434 {
2435 ASSERT(f);
2436
2437 if (!f->prealloc_done)
2438 PreallocHelper(f);
2439
2440 if (f->split_f)
2441 {
2442 TRACE("#S# ---\n");
2443 TRACE(TRACE_RDWR_FORMAT, "#S# WriteF()",
2444 GetFD(f), GetFP(f), (u64)f->cur_off, (u64)f->cur_off+count, count,
2445 f->cur_off < f->max_off ? " <" : "" );
2446
2447 File_t ** ptr = f->split_f;
2448 off_t off = f->cur_off;
2449 f->cur_off = 0;
2450
2451 while ( count > 0 )
2452 {
2453 if (!*ptr)
2454 {
2455 enumError err = XCreateSplitFile( XCALL f, ptr-f->split_f );
2456 if (err)
2457 return err;
2458 }
2459
2460 File_t *cur = *ptr;
2461 ASSERT(cur);
2462 TRACE("#S#%zd# off=%llx cur_of=%llx count=%zx fsize=%llx\n",
2463 ptr-f->split_f, (u64)off, (u64)f->cur_off,
2464 count, (u64)cur->split_filesize );
2465
2466 if ( off < cur->split_filesize )
2467 {
2468 // write to this file
2469 const off_t max_count = cur->split_filesize - off;
2470 const size_t stream_count = count < max_count ? count : (size_t)max_count;
2471 enumError err = XWriteAtF( XCALL cur, off, iobuf, stream_count );
2472 if (err)
2473 return err;
2474 f->bytes_written += stream_count;
2475 count -= stream_count;
2476 iobuf = (char*)iobuf + stream_count;
2477 f->cur_off += off + stream_count;
2478 off = 0;
2479 }
2480 else
2481 {
2482 // skip this file
2483 f->cur_off += cur->split_filesize;
2484 off -= cur->split_filesize;
2485 }
2486 ptr++;
2487 }
2488 if ( f->max_off < f->cur_off )
2489 f->max_off = f->cur_off;
2490
2491 TRACE("#S#x# off=%9llx cur_of=%9llx count=%zx\n", (u64)off, (u64)f->cur_off, count );
2492 return ERR_OK;
2493 }
2494
2495 //--------------------------------------------------
2496
2497 ASSERT( f->cur_off == f->file_off ); // no cache while writing
2498 TRACE(TRACE_RDWR_FORMAT, "#F# WriteF()",
2499 GetFD(f), GetFP(f), (u64)f->file_off, (u64)f->file_off+count, count,
2500 f->file_off < f->max_off ? " <" : "" );
2501
2502 bool err;
2503 if (f->fp)
2504 err = count && fwrite(iobuf,count,1,f->fp) != 1;
2505 else if ( f->fd != -1 )
2506 {
2507 err = false;
2508 size_t size = count;
2509 while (size)
2510 {
2511 ssize_t wstat = write(f->fd,iobuf,size);
2512 if ( wstat <= 0 )
2513 {
2514 err = true;
2515 break;
2516 }
2517 size -= wstat;
2518 iobuf = (void*)( (char*)iobuf + wstat );
2519 }
2520 }
2521 else
2522 {
2523 err = true;
2524 errno = 0;
2525 }
2526
2527 if (err)
2528 {
2529 if ( !f->disable_errors && f->last_error != ERR_WRITE_FAILED )
2530 PrintError( XERROR1, ERR_WRITE_FAILED,
2531 "Write failed [%c=%d,%llu+%zu]: %s\n",
2532 GetFT(f), GetFD(f),
2533 (u64)f->file_off, count, f->fname );
2534
2535 f->cur_off = f->file_off = (off_t)-1ll;
2536
2537 if ( f->max_error < ERR_WRITE_FAILED )
2538 f->max_error = ERR_WRITE_FAILED;
2539 return f->last_error = ERR_WRITE_FAILED;
2540 }
2541
2542 f->write_count++;
2543 f->bytes_written += count;
2544 f->file_off += count;
2545 if ( f->max_off < f->file_off )
2546 f->max_off = f->file_off;
2547 f->cur_off = f->file_off;
2548 return ERR_OK;
2549 }
2550
2551 ///////////////////////////////////////////////////////////////////////////////
2552
XReadAtF(XPARM File_t * f,off_t off,void * iobuf,size_t count)2553 enumError XReadAtF ( XPARM File_t * f, off_t off, void * iobuf, size_t count )
2554 {
2555 ASSERT(f);
2556 noTRACE("#F# ReadAtF(fd=%d,o=%llx,%p,n=%zx)\n",f->fd,(u64)off,iobuf,count);
2557 f->cache_info_off = off;
2558 f->cache_info_size = count;
2559 const enumError stat = XSeekF(XCALL f,off);
2560 return stat ? stat : XReadF(XCALL f,iobuf,count);
2561 }
2562
2563 ///////////////////////////////////////////////////////////////////////////////
2564
XWriteAtF(XPARM File_t * f,off_t off,const void * iobuf,size_t count)2565 enumError XWriteAtF ( XPARM File_t * f, off_t off, const void * iobuf, size_t count )
2566 {
2567 ASSERT(f);
2568 noTRACE("#F# WriteAtF(fd=%d,o=%llx,%p,n=%zx)\n",f->fd,(u64)off,iobuf,count);
2569 const enumError stat = XSeekF(XCALL f,off);
2570 return stat ? stat : XWriteF(XCALL f,iobuf,count);
2571 }
2572
2573 ///////////////////////////////////////////////////////////////////////////////
2574
XWriteZeroAtF(XPARM File_t * f,off_t off,size_t count)2575 enumError XWriteZeroAtF ( XPARM File_t * f, off_t off, size_t count )
2576 {
2577 TRACE(TRACE_RDWR_FORMAT, "#F# WriteZeroAtF()",
2578 GetFD(f), GetFP(f), (u64)off, (u64)off+count, count,
2579 off < f->max_off ? " <" : "" );
2580
2581 enumError err = XSeekF(XCALL f,off);
2582 while ( !err && count > 0 )
2583 {
2584 const size_t wsize = count < sizeof(zerobuf) ? count : sizeof(zerobuf);
2585 err = XWriteF(XCALL f, zerobuf, wsize );
2586 count -= wsize;
2587 }
2588 return err;
2589 }
2590
2591 ///////////////////////////////////////////////////////////////////////////////
2592 // [zero]
2593
XZeroAtF(XPARM File_t * f,off_t off,size_t count)2594 enumError XZeroAtF ( XPARM File_t * f, off_t off, size_t count )
2595 {
2596 ASSERT(f);
2597 TRACE(TRACE_RDWR_FORMAT, "#F# ZeroAtF()",
2598 GetFD(f), GetFP(f), (u64)off, (u64)off+count, count,
2599 off < f->max_off ? " <" : "" );
2600
2601 if ( !f->is_reading || !S_ISREG(f->st.st_mode) )
2602 return XWriteZeroAtF(XCALL f,off,count);
2603
2604 //----- check file growing
2605
2606 off_t last_off = off + count;
2607 if ( last_off > f->max_off )
2608 {
2609 const off_t max_off = f->max_off;
2610 const enumError err = XSetSizeF( XCALL f, last_off );
2611 if ( err || off >= max_off )
2612 return err;
2613
2614 ASSERT( count > last_off - max_off );
2615 count -= last_off - max_off;
2616 last_off = max_off;
2617 }
2618
2619 //----- try to align to blocks
2620
2621 char buf[0x20000];
2622 const size_t blocksize = f->st.st_blksize < HD_SECTOR_SIZE
2623 ? HD_SECTOR_SIZE
2624 : f->st.st_blksize > sizeof(buf)
2625 ? sizeof(buf)
2626 : f->st.st_blksize;
2627
2628 if ( off/blocksize != (last_off-1)/blocksize )
2629 {
2630 // align to blocksize
2631 const size_t count1 = blocksize - off % blocksize;
2632 ASSERT( count1 < count );
2633 const enumError err = XZeroAtF(XCALL f,off,count1);
2634 if (err)
2635 return err;
2636 off += count1;
2637 count -= count1;
2638 }
2639
2640 //----- the main loop
2641
2642 while ( count > 0 )
2643 {
2644 //--- read data
2645
2646 const size_t rsize = count < sizeof(buf) ? count : sizeof(buf);
2647 const enumError err = XReadAtF(XCALL f,off,buf,rsize);
2648 if (err)
2649 return err;
2650
2651 //--- iterate read data
2652
2653 char * ptr = buf;
2654 char * end = buf + rsize;
2655
2656 while ( ptr < end )
2657 {
2658 //--- find begining of non zero data
2659
2660 while ( ptr < end && !*ptr )
2661 ptr++;
2662
2663 //--- set start block aligned
2664
2665 size_t start = ((ptr-buf)/blocksize)*blocksize;
2666 ptr = buf + start + blocksize;
2667
2668 //--- find zero block
2669
2670 while ( ptr < end )
2671 {
2672 char * bl_end = ptr + blocksize;
2673 if ( bl_end > end )
2674 bl_end = end;
2675 while ( ptr < bl_end && !*ptr )
2676 ptr++;
2677 if ( ptr == bl_end )
2678 break;
2679 ptr = bl_end;
2680 }
2681
2682 if ( ptr > end )
2683 ptr = end;
2684
2685 const enumError err = XWriteZeroAtF(XCALL f, off+start, ptr-buf-start );
2686 if (!err)
2687 return err;
2688 }
2689
2690 off += rsize;
2691 count -= rsize;
2692 }
2693 return ERR_OK;
2694 }
2695
2696 //
2697 ///////////////////////////////////////////////////////////////////////////////
2698 ///////////////////////////////////////////////////////////////////////////////
2699
WrapperReadSector(void * handle,u32 lba,u32 count,void * iobuf)2700 int WrapperReadSector ( void * handle, u32 lba, u32 count, void * iobuf )
2701 {
2702 ASSERT(handle);
2703 SuperFile_t * sf = (SuperFile_t*)handle;
2704
2705 TRACE("WrapperReadSector(fd=%d,lba=%x,count=%x) sector-size=%u\n",
2706 GetFD(&sf->f), lba, count, sf->f.sector_size );
2707
2708 if (SIGINT_level>1)
2709 return ERR_INTERRUPT;
2710
2711 return ReadAtF(
2712 &sf->f,
2713 (off_t)lba * sf->f.sector_size,
2714 iobuf,
2715 count * sf->f.sector_size );
2716 }
2717
2718 ///////////////////////////////////////////////////////////////////////////////
2719
WrapperWriteSector(void * handle,u32 lba,u32 count,void * iobuf)2720 int WrapperWriteSector ( void * handle, u32 lba, u32 count, void * iobuf )
2721 {
2722 ASSERT(handle);
2723 SuperFile_t * sf = (SuperFile_t*)handle;
2724
2725 TRACE("WBFS: WrapperWriteSector(fd=%d,lba=%x,count=%x) sector-size=%u\n",
2726 GetFD(&sf->f), lba, count, sf->f.sector_size );
2727
2728 if (SIGINT_level>1)
2729 return ERR_INTERRUPT;
2730
2731 return WriteAtF(
2732 &sf->f,
2733 (off_t)lba * sf->f.sector_size,
2734 iobuf,
2735 count * sf->f.sector_size );
2736 }
2737
2738 ///////////////////////////////////////////////////////////////////////////////
2739
GetFD(const File_t * f)2740 int GetFD ( const File_t * f )
2741 {
2742 return !f ? -1 : f->split_used > 0 ? f->split_f[0]->fd : f->fd;
2743 }
2744
2745 ///////////////////////////////////////////////////////////////////////////////
2746
GetFP(const File_t * f)2747 FILE * GetFP ( const File_t * f )
2748 {
2749 return !f ? 0 : f->split_used > 0 ? f->split_f[0]->fp : f->fp;
2750 }
2751
2752 ///////////////////////////////////////////////////////////////////////////////
2753
GetFT(const File_t * f)2754 char GetFT ( const File_t * f )
2755 {
2756 if (!f)
2757 return '%';
2758
2759 if ( f->split_used > 0 )
2760 f = f->split_f[0];
2761
2762 return f->fp ? 'S' : f->fd != -1 ? 'F' : '-';
2763 }
2764
2765 ///////////////////////////////////////////////////////////////////////////////
2766
IsOpenF(const File_t * f)2767 bool IsOpenF ( const File_t * f )
2768 {
2769 return f && ( f->split_used > 0 ? f->split_f[0]->fd : f->fd ) != -1;
2770 }
2771
2772 ///////////////////////////////////////////////////////////////////////////////
2773
IsSplittedF(const File_t * f)2774 bool IsSplittedF ( const File_t * f )
2775 {
2776 return f && f->split_used > 0;
2777 }
2778
2779 ///////////////////////////////////////////////////////////////////////////////
2780
IsDirectory(ccp fname,bool answer_if_empty)2781 bool IsDirectory ( ccp fname, bool answer_if_empty )
2782 {
2783 if ( !fname || !*fname )
2784 return answer_if_empty;
2785
2786 if ( *fname == '-' && !fname[1] )
2787 return false;
2788
2789 if ( fname[strlen(fname)-1] == '/' )
2790 return true;
2791
2792 struct stat st;
2793 return !stat(fname,&st) && S_ISDIR(st.st_mode);
2794 }
2795
2796 ///////////////////////////////////////////////////////////////////////////////
2797
GetFileMode(mode_t mode)2798 enumFileMode GetFileMode ( mode_t mode )
2799 {
2800 return S_ISREG(mode) ? FM_PLAIN
2801 : S_ISBLK(mode) ? FM_BLKDEV
2802 : S_ISCHR(mode) ? FM_CHRDEV
2803 : 0;
2804 }
2805
GetFileModeText(enumFileMode mode,bool longtext,ccp fail_text)2806 ccp GetFileModeText ( enumFileMode mode, bool longtext, ccp fail_text )
2807 {
2808 switch (mode)
2809 {
2810 case FM_PLAIN:
2811 return longtext ? "PLAIN FILE" : "PLAIN";
2812
2813 case FM_BLKDEV:
2814 return longtext ? "BLOCK DEVICE" : "BLOCK";
2815
2816 case FM_CHRDEV:
2817 return longtext ? "CHARACTER DEVICE" : "CHAR";
2818
2819 default:
2820 return fail_text;
2821 }
2822 }
2823
2824 ///////////////////////////////////////////////////////////////////////////////
2825
CreatePath(ccp fname)2826 enumError CreatePath ( ccp fname )
2827 {
2828 TRACE("CreatePath(%s)\n",fname);
2829
2830 char buf[PATH_MAX], *dest = buf;
2831 StringCopyS(buf,sizeof(buf),fname);
2832
2833 for(;;)
2834 {
2835 // skip slashes
2836 while ( *dest == '/' )
2837 dest++;
2838
2839 // search end of current directory
2840 while ( *dest && *dest != '/' )
2841 dest++;
2842 if (!*dest)
2843 break;
2844
2845 *dest = 0;
2846 if ( mkdir(buf,0777) && errno != EEXIST && !IsDirectory(buf,0) )
2847 {
2848 noTRACE("CREATE-DIR: %s -> err=%d (ENOTDIR=%d)\n",buf,errno,ENOTDIR);
2849 if ( errno == ENOTDIR )
2850 {
2851 while ( dest > buf && *dest != '/' )
2852 dest--;
2853 if ( dest > buf )
2854 *dest = 0;
2855 }
2856 return ERROR1( ERR_CANT_CREATE_DIR,
2857 errno == ENOTDIR
2858 ? "Not a directory: %s\n"
2859 : "Can't create directory: %s\n", buf );
2860 }
2861 TRACE("CREATE-DIR: %s -> OK\n",buf);
2862 *dest++ = '/';
2863 }
2864 return ERR_OK;
2865 }
2866
2867 ///////////////////////////////////////////////////////////////////////////////
2868
GetFileSize(ccp path1,ccp path2,s64 not_found_val,FileAttrib_t * fatt,bool fatt_max)2869 s64 GetFileSize
2870 (
2871 ccp path1, // NULL or part 1 of path
2872 ccp path2, // NULL or part 2 of path
2873 s64 not_found_val, // return value if no regular file found
2874 FileAttrib_t * fatt, // not NULL: store file attributes
2875 bool fatt_max // true: store max values to 'fatt'
2876 )
2877 {
2878 char pathbuf[PATH_MAX];
2879 ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2880 TRACE("GetFileSize(%s,%lld)\n",path,not_found_val);
2881
2882 struct stat st;
2883 if ( stat(path,&st) || !S_ISREG(st.st_mode) )
2884 {
2885 if ( fatt && !fatt_max )
2886 memset(fatt,0,sizeof(*fatt));
2887 return not_found_val;
2888 }
2889
2890 if (fatt)
2891 CopyFileAttribStat(fatt,&st,fatt_max);
2892
2893 return st.st_size;
2894 }
2895
2896 ///////////////////////////////////////////////////////////////////////////////
2897
LoadFile(ccp path1,ccp path2,size_t skip,void * data,size_t size,bool silent,FileAttrib_t * fatt,bool fatt_max)2898 enumError LoadFile
2899 (
2900 ccp path1, // NULL or part #1 of path
2901 ccp path2, // NULL or part #2 of path
2902 size_t skip, // skip num of bytes before reading
2903 void * data, // destination buffer, size = 'size'
2904 size_t size, // size to read
2905 bool silent, // true: suppress printing of error messages
2906 FileAttrib_t * fatt, // not NULL: store file attributes
2907 bool fatt_max // true: store max values to 'fatt'
2908 )
2909 {
2910 // [[2do]] error handling
2911
2912 ASSERT(data);
2913 if ( fatt && !fatt_max )
2914 memset(fatt,0,sizeof(*fatt));
2915
2916 if (!size)
2917 return ERR_OK;
2918
2919 char pathbuf[PATH_MAX];
2920 ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2921 TRACE("LoadFile(%s,%zu,%zu,%d)\n",path,skip,size,silent);
2922
2923 FILE * f = fopen(path,"rb");
2924 if (!f)
2925 {
2926 if (!silent)
2927 ERROR1(ERR_CANT_OPEN,"Can't open file: %s\n",path);
2928 return ERR_CANT_OPEN;
2929 }
2930
2931 if (fatt)
2932 {
2933 struct stat st;
2934 if (!fstat(fileno(f),&st))
2935 CopyFileAttribStat(fatt,&st,fatt_max);
2936 }
2937
2938 if ( skip > 0 )
2939 fseek(f,skip,SEEK_SET);
2940
2941 size_t read_stat = fread(data,1,size,f);
2942 fclose(f);
2943
2944 if ( read_stat == size )
2945 return ERR_OK;
2946
2947 noPRINT("D=%p, s=%zu/%zu: %s\n",data,read_stat,size,path);
2948 if ( read_stat >= 0 && read_stat < size )
2949 memset((char*)data+read_stat,0,size-read_stat);
2950
2951 return ERR_WARNING;
2952 }
2953
2954 ///////////////////////////////////////////////////////////////////////////////
2955
LoadFileAlloc(ccp path1,ccp path2,size_t skip,u8 ** res_data,size_t * res_size,size_t max_size,bool silent,FileAttrib_t * fatt,bool fatt_max)2956 enumError LoadFileAlloc
2957 (
2958 ccp path1, // NULL or part #1 of path
2959 ccp path2, // NULL or part #2 of path
2960 size_t skip, // skip num of bytes before reading
2961 u8 ** res_data, // result: free existing data, store ptr to alloc data
2962 // always one more byte is alloced and set to NULL
2963 size_t * res_size, // result: size of 'res_data'
2964 size_t max_size, // >0: a file size limit
2965 bool silent, // true: suppress printing of error messages
2966 FileAttrib_t * fatt, // not NULL: store file attributes
2967 bool fatt_max // true: store max values to 'fatt'
2968 )
2969 {
2970 DASSERT(res_data);
2971 DASSERT(res_size);
2972
2973 //--- clear return data
2974
2975 if (res_data)
2976 *res_data = 0;
2977
2978 if (res_size)
2979 *res_size = 0;
2980
2981 if ( fatt && !fatt_max )
2982 memset(fatt,0,sizeof(*fatt));
2983
2984
2985 //--- get file size
2986
2987 char pathbuf[PATH_MAX];
2988 ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
2989
2990 const s64 size = GetFileSize(path,0,-1,0,0);
2991 if ( size == -1 )
2992 {
2993 if (!silent)
2994 ERROR0(ERR_SYNTAX,"File not found: %s\n",path);
2995 return ERR_SYNTAX;
2996 }
2997
2998 if ( max_size && size > max_size )
2999 {
3000 if (!silent)
3001 ERROR0(ERR_INVALID_FILE,"File to large: %s\n",path);
3002 return ERR_INVALID_FILE;
3003 }
3004
3005 u8 *data = MALLOC(size+1);
3006 enumError err = LoadFile(path,0,skip,data,size,silent,fatt,fatt_max);
3007 if (err)
3008 {
3009 FREE(data);
3010 return err;
3011 }
3012
3013 if (res_data)
3014 {
3015 data[size] = 0;
3016 *res_data = data;
3017 }
3018 else
3019 FREE(data);
3020
3021 if (res_size)
3022 *res_size = size;
3023
3024 return err;
3025 }
3026
3027 ///////////////////////////////////////////////////////////////////////////////
3028
CheckCreateFile(ccp fname,bool detect_stdout,bool overwrite,bool silent,struct stat * st)3029 enumError CheckCreateFile
3030 (
3031 // returns:
3032 // ERR_WARNING: source is "-" (stdout) => 'st' is zeroed
3033 // ERR_ALREADY_EXISTS: file already exists
3034 // ERR_WRONG_FILE_TYPE: file exists and file type is wrong
3035 // ERR_OK: file not exist or can be overwritten
3036
3037 ccp fname, // filename to open
3038 bool detect_stdout, // true: detect "-" as stdout
3039 bool overwrite, // true: overwriting is allowed
3040 bool silent, // true: suppress error messages
3041 struct stat *st // not NULL: store file status here
3042 )
3043 {
3044
3045 if ( detect_stdout && fname[0] == '-' && !fname[1] )
3046 {
3047 if (st)
3048 memset(st,0,sizeof(*st));
3049 return ERR_WARNING;
3050 }
3051
3052 struct stat local_st;
3053 if (!st)
3054 st = &local_st;
3055
3056 if (!stat(fname,st))
3057 {
3058 if ( S_ISBLK(st->st_mode) || S_ISCHR(st->st_mode) )
3059 {
3060 if (!silent)
3061 ERROR0( ERR_ALREADY_EXISTS,
3062 "Can't write to %s device: %s\n",
3063 S_ISBLK(st->st_mode) ? "block" : "character", fname );
3064 return ERR_WRONG_FILE_TYPE;
3065 }
3066
3067 if (!S_ISREG(st->st_mode))
3068 {
3069 if (!silent)
3070 ERROR0( ERR_WRONG_FILE_TYPE,
3071 "Not a plain file: %s\n", fname );
3072 return ERR_WRONG_FILE_TYPE;
3073 }
3074
3075 if (!overwrite)
3076 {
3077 if (!silent)
3078 ERROR0( ERR_ALREADY_EXISTS,
3079 "File already exists: %s\n", fname );
3080 return ERR_ALREADY_EXISTS;
3081 }
3082 }
3083 return ERR_OK;
3084 }
3085
3086 ///////////////////////////////////////////////////////////////////////////////
3087
SaveFile(ccp path1,ccp path2,bool overwrite,bool create_dir,const void * data,size_t size,bool silent)3088 enumError SaveFile
3089 (
3090 ccp path1, // NULL or part #1 of path
3091 ccp path2, // NULL or part #2 of path
3092 bool overwrite, // true: overwrite existing files
3093 bool create_dir, // true: create path automatically
3094 const void * data, // pointer to data
3095 size_t size, // size of 'data'
3096 bool silent // true: suppress error messages
3097 )
3098 {
3099 ASSERT(data);
3100
3101 char pathbuf[PATH_MAX];
3102 ccp path = PathCatPP(pathbuf,sizeof(pathbuf),path1,path2);
3103 TRACE("SaveFile(%s,%zx,%d)\n",path,size,silent);
3104
3105 enumError err = CheckCreateFile(path,false,overwrite,silent,0);
3106 if (err)
3107 return err;
3108
3109 FILE * f = fopen(path,"wb");
3110 if (!f)
3111 {
3112 if (create_dir)
3113 {
3114 CreatePath(path);
3115 f = fopen(path,"wb");
3116 }
3117
3118 if (!f)
3119 {
3120 if (!silent)
3121 ERROR1(ERR_CANT_CREATE,"Can't create file: %s\n",path);
3122 return ERR_CANT_CREATE;
3123 }
3124 }
3125
3126 size_t stat = fwrite(data,1,size,f);
3127 if ( stat != size )
3128 {
3129 err = ERR_WRITE_FAILED;
3130 if (!silent)
3131 ERROR1(err,"Write to file failed: %s\n",path);
3132 }
3133 fclose(f);
3134
3135 return err;
3136 }
3137
3138 ///////////////////////////////////////////////////////////////////////////////
3139 ///////////////////////////////////////////////////////////////////////////////
3140
ClearFileID(File_t * f)3141 void ClearFileID
3142 (
3143 File_t * f
3144 )
3145 {
3146 DASSERT(f);
3147 memset( f->id6_src, 0, sizeof(f->id6_src) );
3148 memset( f->id6_dest, 0, sizeof(f->id6_dest) );
3149 }
3150
3151 ///////////////////////////////////////////////////////////////////////////////
3152
SetFileID(File_t * f,const void * new_id,int id_length)3153 void SetFileID
3154 (
3155 File_t * f,
3156 const void * new_id,
3157 int id_length
3158 )
3159 {
3160 DASSERT(f);
3161 DASSERT(id_length>=0);
3162
3163 memset( f->id6_src, 0, sizeof(f->id6_src) );
3164 memcpy( f->id6_src, new_id, id_length < 6 ? id_length : 6 );
3165 memcpy( f->id6_dest, f->id6_src, sizeof(f->id6_dest) );
3166 noPRINT("$$$ SetFileID: |%s|%s|\n",f->id6_src,f->id6_dest);
3167 }
3168
3169 ///////////////////////////////////////////////////////////////////////////////
3170
SetPatchFileID(File_t * f,const void * new_id,int id_length)3171 bool SetPatchFileID
3172 (
3173 File_t * f,
3174 const void * new_id,
3175 int id_length
3176 )
3177 {
3178 DASSERT(f);
3179 DASSERT(id_length>=0);
3180
3181 memset( f->id6_src, 0, sizeof(f->id6_src) );
3182 memcpy( f->id6_src, new_id, id_length < 6 ? id_length : 6 );
3183 const bool stat = CopyPatchDiscId( f->id6_dest, f->id6_src );
3184 noPRINT("$$$ SetPatchFileID: |%s|%s| stat=%d\n",f->id6_src,f->id6_dest,stat);
3185 return stat;
3186 }
3187
3188 //
3189 ///////////////////////////////////////////////////////////////////////////////
3190 /////////////// FileAttrib_t ///////////////
3191 ///////////////////////////////////////////////////////////////////////////////
3192
NormalizeFileAttrib(FileAttrib_t * fa)3193 FileAttrib_t * NormalizeFileAttrib
3194 (
3195 FileAttrib_t * fa // valid attribute
3196 )
3197 {
3198 DASSERT(fa);
3199
3200 if ( fa->size < 0 )
3201 fa->size = 0;
3202
3203 time_t mtime = fa->mtime;
3204 if (!mtime)
3205 {
3206 mtime = fa->itime;
3207 if (!mtime)
3208 {
3209 mtime = fa->ctime;
3210 if (!mtime)
3211 mtime = fa->atime;
3212 }
3213 }
3214 fa->mtime = mtime;
3215
3216 if (!fa->itime)
3217 fa->itime = fa->ctime > mtime ? fa->ctime : mtime;
3218
3219 if (!fa->ctime)
3220 fa->ctime = fa->itime > mtime ? fa->itime : mtime;
3221
3222 if (!fa->atime)
3223 fa->atime = fa->itime > fa->ctime ? fa->itime : fa->ctime;
3224
3225 return fa;
3226 }
3227
3228 //-----------------------------------------------------------------------------
3229
MaxFileAttrib(FileAttrib_t * dest,const FileAttrib_t * src)3230 FileAttrib_t * MaxFileAttrib
3231 (
3232 FileAttrib_t * dest, // source and destination attribute
3233 const FileAttrib_t * src // NULL or second source attribute
3234 )
3235 {
3236 DASSERT(dest);
3237 if (src)
3238 {
3239 if ( dest->size < src->size )
3240 dest->size = src->size;
3241 if ( dest->itime < src->itime )
3242 dest->itime = src->itime;
3243 if ( dest->mtime < src->mtime )
3244 dest->mtime = src->mtime;
3245 if ( dest->ctime < src->ctime )
3246 dest->ctime = src->ctime;
3247 if ( dest->atime < src->atime )
3248 dest->atime = src->atime;
3249 }
3250 return dest;
3251 }
3252
3253 //-----------------------------------------------------------------------------
3254
CopyFileAttrib(FileAttrib_t * dest,const FileAttrib_t * src)3255 FileAttrib_t * CopyFileAttrib
3256 (
3257 FileAttrib_t * dest, // valid destination attribute
3258 const FileAttrib_t * src // valid source attribute
3259 )
3260 {
3261 DASSERT(src);
3262 DASSERT(dest);
3263
3264 memcpy(dest,src,sizeof(*dest));
3265 return NormalizeFileAttrib(dest);
3266 }
3267
3268 //-----------------------------------------------------------------------------
3269
CopyFileAttribStat(FileAttrib_t * dest,const struct stat * src,bool maximize)3270 FileAttrib_t * CopyFileAttribStat
3271 (
3272 FileAttrib_t * dest, // valid destination attribute
3273 const struct stat * src, // NULL or source
3274 bool maximize // true store max values to 'dest'
3275 )
3276 {
3277 DASSERT(dest);
3278
3279 if (src)
3280 {
3281 FileAttrib_t temp_fatt;
3282 FileAttrib_t * fatt = maximize ? &temp_fatt : dest;
3283
3284 memset(fatt,0,sizeof(*fatt));
3285 fatt->size = src->st_size;
3286
3287 if ( S_ISREG(src->st_mode) )
3288 {
3289 fatt->mtime = src->st_mtime;
3290 fatt->ctime = src->st_ctime;
3291 fatt->atime = src->st_atime;
3292 }
3293 NormalizeFileAttrib(fatt);
3294
3295 if (maximize)
3296 MaxFileAttrib(dest,fatt);
3297 }
3298 else if (!maximize)
3299 memset(dest,0,sizeof(*dest));
3300
3301 return dest;
3302 }
3303
3304 //-----------------------------------------------------------------------------
3305
CopyFileAttribInode(FileAttrib_t * dest,const struct wbfs_inode_info_t * src,off_t size)3306 FileAttrib_t * CopyFileAttribInode
3307 ( FileAttrib_t * dest, const struct wbfs_inode_info_t * src, off_t size )
3308 {
3309 ASSERT(src);
3310 ASSERT(dest);
3311
3312 dest->size = size;
3313 if (wbfs_is_inode_info_valid(0,src))
3314 {
3315 dest->itime = ntoh64(src->itime);
3316 dest->mtime = ntoh64(src->mtime);
3317 dest->ctime = ntoh64(src->ctime);
3318 dest->atime = ntoh64(src->atime);
3319 }
3320
3321 return NormalizeFileAttrib(dest);
3322 }
3323
3324 //-----------------------------------------------------------------------------
3325
CopyFileAttribDiscInfo(FileAttrib_t * dest,const struct WDiscInfo_t * src)3326 FileAttrib_t * CopyFileAttribDiscInfo
3327 ( FileAttrib_t * dest, const struct WDiscInfo_t * src )
3328 {
3329 ASSERT(src);
3330 ASSERT(dest);
3331
3332 return CopyFileAttribInode(dest,&src->dhead.iinfo,src->size);
3333 }
3334
3335 //
3336 ///////////////////////////////////////////////////////////////////////////////
3337 /////////////// split file support ///////////////
3338 ///////////////////////////////////////////////////////////////////////////////
3339
CalcSplitFilename(char * buf,size_t buf_size,ccp path,enumOFT oft)3340 int CalcSplitFilename ( char * buf, size_t buf_size, ccp path, enumOFT oft )
3341 {
3342 const int needed_space = oft == OFT_WBFS ? 6 : 7;
3343 const int max_path_len = PATH_MAX - needed_space;
3344
3345 if (!path)
3346 path = "";
3347 TRACE("CalcSplitFilename(%s,%d)\n",path,oft);
3348
3349 size_t plen = strlen(path);
3350 if ( plen > 0 && oft == OFT_WBFS )
3351 plen--;
3352 if ( plen > max_path_len )
3353 plen = max_path_len;
3354 ccp path_end = path + plen;
3355
3356 char * dest = buf;
3357 if ( buf_size > needed_space )
3358 {
3359 char * end = dest + buf_size - needed_space;
3360 while ( dest < end && path < path_end )
3361 {
3362 if ( *path == '%' )
3363 {
3364 *dest++ = '%';
3365 *dest++ = '%';
3366 path++;
3367 }
3368 else
3369 *dest++ = *path++;
3370 }
3371
3372 if ( oft != OFT_WBFS )
3373 *dest++ = '.';
3374 *dest++ = '%';
3375 *dest++ = '0';
3376 *dest++ = '1';
3377 *dest++ = 'u';
3378 }
3379 *dest = 0;
3380 return dest-buf;
3381 }
3382
3383 //-----------------------------------------------------------------------------
3384
AllocSplitFilename(ccp path,enumOFT oft)3385 char * AllocSplitFilename ( ccp path, enumOFT oft )
3386 {
3387 char buf[2*PATH_MAX];
3388 CalcSplitFilename(buf,sizeof(buf),path,oft);
3389 return STRDUP(buf);
3390 }
3391
3392 //
3393 ///////////////////////////////////////////////////////////////////////////////
3394 /////////////// GetFileSystemMap() ///////////////
3395 ///////////////////////////////////////////////////////////////////////////////
3396 #if HAVE_FIEMAP
3397 ///////////////////////////////////////////////////////////////////////////////
3398
GetFileMapHelper_FIEMAP(FileMap_t * fm,File_t * file,struct fiemap * fmap,uint n_elem)3399 static enumError GetFileMapHelper_FIEMAP
3400 (
3401 FileMap_t * fm, // file map
3402 File_t * file, // file to analyze
3403 struct fiemap * fmap, // valid fiemap structure
3404 uint n_elem // number of 'fmap->fm_extents' elements
3405 )
3406 {
3407 DASSERT(fm);
3408 DASSERT(file);
3409 DASSERT(fmap);
3410 DASSERT(n_elem>0);
3411
3412 memset(fmap,0,sizeof(fmap));
3413
3414 u64 last_off = 0;
3415 while ( last_off < file->st.st_size )
3416 {
3417 fmap->fm_start = last_off;
3418 fmap->fm_length = file->st.st_size - last_off;
3419 fmap->fm_flags = FIEMAP_FLAG_SYNC;
3420 fmap->fm_extent_count = n_elem;
3421 int stat = ioctl(file->fd,FS_IOC_FIEMAP,fmap);
3422 if ( stat < 0 )
3423 return ERR_WARNING; // delayed error message
3424
3425 PRINT("STAT=%d, N=%d\n",stat,fmap->fm_mapped_extents);
3426 if (!fmap->fm_mapped_extents)
3427 break;
3428
3429 const struct fiemap_extent *fe = fmap->fm_extents;
3430 const struct fiemap_extent *fe_end = fe + fmap->fm_mapped_extents;
3431 for ( ; fe < fe_end; fe++ )
3432 {
3433 AppendFileMap( fm, fe->fe_logical + file->split_off,
3434 fe->fe_physical, fe->fe_length);
3435 noPRINT(" -> %9llx %9llx %9llx\n",
3436 fe->fe_logical, fe->fe_physical, fe->fe_length );
3437 }
3438 if ( fe[-1].fe_flags & FIEMAP_EXTENT_LAST )
3439 break;
3440 last_off = fe[-1].fe_logical + fe[-1].fe_length;
3441 }
3442
3443 return ERR_OK;
3444 }
3445
3446 //////////////////////////////////////////////////////////////////////////////
3447
GetFileSystemMap_FIEMAP(FileMap_t * fm,File_t * file)3448 static enumError GetFileSystemMap_FIEMAP
3449 (
3450 FileMap_t * fm, // file map
3451 File_t * file // file to analyze
3452 )
3453 {
3454 DASSERT(fm);
3455 DASSERT(file);
3456
3457 struct fiemap *fmap;
3458 const uint MAX_ELEM = 1024;
3459 const uint ALLOC_SIZE = sizeof(*fmap) + sizeof(*fmap->fm_extents) * MAX_ELEM;
3460 PRINT("ALLOC FIEMAP: %zu + %zu * %u = %u\n",
3461 sizeof(*fmap), sizeof(*fmap->fm_extents), MAX_ELEM, ALLOC_SIZE );
3462 fmap = MALLOC(ALLOC_SIZE);
3463
3464 enumError err = ERR_OK;
3465 if (file->split_f)
3466 {
3467 File_t **end, **ptr = file->split_f;
3468 for ( end = ptr + file->split_used; err == ERR_OK && ptr < end; ptr++ )
3469 err = GetFileMapHelper_FIEMAP(fm,*ptr,fmap,MAX_ELEM);
3470 }
3471 else
3472 err = GetFileMapHelper_FIEMAP(fm,file,fmap,MAX_ELEM);
3473
3474 FREE(fmap);
3475 return err;
3476 }
3477
3478 ///////////////////////////////////////////////////////////////////////////////
3479 #endif // HAVE_FIEMAP
3480 ///////////////////////////////////////////////////////////////////////////////
3481 #if HAVE_FIBMAP
3482 ///////////////////////////////////////////////////////////////////////////////
3483
GetFileSystemMap_FIBMAP(FileMap_t * fm,File_t * file)3484 static enumError GetFileSystemMap_FIBMAP
3485 (
3486 FileMap_t * fm, // file map
3487 File_t * file // file to analyze
3488 )
3489 {
3490 DASSERT(fm);
3491 DASSERT(file);
3492
3493 if (file->split_f)
3494 {
3495 File_t **end, **ptr = file->split_f;
3496 for ( end = ptr + file->split_used; ptr < end; ptr++ )
3497 {
3498 enumError err = GetFileSystemMap_FIBMAP(fm,*ptr);
3499 if (err)
3500 return err;
3501 }
3502 return ERR_OK;
3503 }
3504
3505 int blocksize;
3506 if ( ioctl(file->fd,FIGETBSZ,&blocksize) < 0 )
3507 return ERROR1(ERR_READ_FAILED,
3508 "Can't read block size (FIGETBSZ): %s\n",file->fname);
3509
3510 uint block, n_blocks = ( file->st.st_size + blocksize - 1 ) / blocksize;
3511 PRINT("FIBMAP: %u blocks * %u bytes\n",n_blocks,blocksize);
3512
3513 for ( block = 0; block < n_blocks; block++ )
3514 {
3515 uint param = block;
3516 if ( ioctl(file->fd,FIBMAP,¶m) < 0 )
3517 return ERR_WARNING; // delayed error message
3518
3519 AppendFileMap( fm, block * (u64)blocksize + file->split_off,
3520 param * (u64)blocksize, blocksize );
3521 }
3522
3523 return ERR_OK;
3524 }
3525
3526 ///////////////////////////////////////////////////////////////////////////////
3527 #endif // HAVE_FIBMAP
3528 ///////////////////////////////////////////////////////////////////////////////
3529
HaveFileSystemMapSupport()3530 bool HaveFileSystemMapSupport()
3531 {
3532 #if HAVE_FIEMAP || HAVE_FIBMAP || defined(__CYGWIN__) && defined(TEST)
3533 return true;
3534 #else
3535 return false;
3536 #endif
3537 }
3538
3539 ///////////////////////////////////////////////////////////////////////////////
3540
GetFileSystemMap(FileMap_t * fm,bool init_fm,File_t * file)3541 enumError GetFileSystemMap
3542 (
3543 FileMap_t * fm, // file map
3544 bool init_fm, // true: initialize 'fm', false: reset 'fm'
3545 File_t * file // file to analyze
3546 )
3547 {
3548 DASSERT(fm);
3549 DASSERT(file);
3550 if (init_fm)
3551 InitializeFileMap(fm);
3552 else
3553 ResetFileMap(fm);
3554
3555 #if HAVE_FIEMAP || HAVE_FIBMAP
3556
3557 enumError stat = ERR_JOB_IGNORED;
3558 if (S_ISREG(file->st.st_mode))
3559 {
3560 #if HAVE_FIEMAP
3561
3562 // first, we try FIEMAP
3563 stat = GetFileSystemMap_FIEMAP(fm,file);
3564 if (!stat)
3565 return ERR_OK;
3566 #endif
3567
3568 #if HAVE_FIBMAP
3569
3570 // if failed, we try FIBMAP
3571 stat = GetFileSystemMap_FIBMAP(fm,file);
3572 if (!stat)
3573 return ERR_OK;
3574 #endif
3575
3576 if ( stat == ERR_WARNING )
3577 {
3578 #if HAVE_FIEMAP && HAVE_FIBMAP
3579 return ERROR1(ERR_READ_FAILED,
3580 "Can't read file mapping (FIEMAP+FIBMAP failed): %s\n",file->fname);
3581 #elif HAVE_FIEMAP
3582 return ERROR1(ERR_READ_FAILED,
3583 "Can't read file mapping (FIEMAP failed): %s\n",file->fname);
3584 #elif HAVE_FIBMAP
3585 return ERROR1(ERR_READ_FAILED,
3586 "Can't read file mapping (FIBMAP failed): %s\n",file->fname);
3587 #endif
3588 }
3589 }
3590 return stat;
3591
3592 #elif defined(__CYGWIN__)
3593
3594 enumError err = ERR_OK;
3595 if (file->split_f)
3596 {
3597 File_t **end, **ptr = file->split_f;
3598 for ( end = ptr + file->split_used; err == ERR_OK && ptr < end; ptr++ )
3599 if (GetWinFileMap(fm,(*ptr)->fd,(*ptr)->split_off,(*ptr)->st.st_size))
3600 err = ERR_READ_FAILED;
3601 }
3602 else
3603 if (GetWinFileMap(fm,file->fd,file->split_off,file->st.st_size))
3604 err = ERR_READ_FAILED;
3605
3606 return err
3607 ? ERROR0(err,"Can't read file mapping: %s\n",file->fname)
3608 : ERR_OK;
3609
3610 #else
3611 return ERR_JOB_IGNORED;
3612 #endif
3613 }
3614
3615 //
3616 ///////////////////////////////////////////////////////////////////////////////
3617 /////////////// cygwin support ///////////////
3618 ///////////////////////////////////////////////////////////////////////////////
3619
3620 #ifdef __CYGWIN__
3621
IsWindowsDriveSpec(ccp src)3622 int IsWindowsDriveSpec ( ccp src )
3623 {
3624 if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' )
3625 && src[1] == ':' )
3626 {
3627 if (!src[2])
3628 return 2;
3629
3630 if ( src[2] == '/' || src[2] == '\\' )
3631 return 3;
3632 }
3633 return 0;
3634 }
3635
3636 #endif // __CYGWIN__
3637
3638 ///////////////////////////////////////////////////////////////////////////////
3639
3640 #ifdef __CYGWIN__
3641
NormalizeFilenameCygwin(char * buf,size_t bufsize,ccp src)3642 int NormalizeFilenameCygwin ( char * buf, size_t bufsize, ccp src )
3643 {
3644 static char prefix[] = "/cygdrive/";
3645
3646 if ( bufsize < sizeof(prefix) + 5 )
3647 {
3648 *buf = 0;
3649 return 0;
3650 }
3651
3652 char * end = buf + bufsize - 1;
3653 char * dest = buf;
3654
3655 if ( ( *src >= 'a' && *src <= 'z' || *src >= 'A' && *src <= 'Z' )
3656 && src[1] == ':'
3657 && ( src[2] == 0 || src[2] == '/' || src[2] == '\\' ))
3658 {
3659 memcpy(buf,prefix,sizeof(prefix));
3660 dest = buf + sizeof(prefix)-1;
3661 *dest++ = tolower((int)*src); // cygwin needs the '(int)'
3662 *dest = 0;
3663 if (IsDirectory(buf,false))
3664 {
3665 *dest++ = '/';
3666 src += 2;
3667 if (*src)
3668 src++;
3669 }
3670 else
3671 dest = buf;
3672 }
3673 ASSERT( dest < buf + bufsize );
3674
3675 while ( dest < end && *src )
3676 if ( *src == '\\' )
3677 {
3678 *dest++ = '/';
3679 src++;
3680 }
3681 else
3682 *dest++ = *src++;
3683
3684 *dest = 0;
3685 ASSERT( dest < buf + bufsize );
3686 return dest - buf;
3687 }
3688
3689 #endif // __CYGWIN__
3690
3691 ///////////////////////////////////////////////////////////////////////////////
3692
3693 #ifdef __CYGWIN__
3694
AllocNormalizedFilenameCygwin(ccp source)3695 char * AllocNormalizedFilenameCygwin ( ccp source )
3696 {
3697 char buf[PATH_MAX];
3698 const int len = NormalizeFilenameCygwin(buf,sizeof(buf),source);
3699 char * result = MALLOC(len+1);
3700 memcpy(result,buf,len+1);
3701 ASSERT(buf[len]==0);
3702 return result;
3703 }
3704
3705 #endif // __CYGWIN__
3706
3707 ///////////////////////////////////////////////////////////////////////////////
3708
SetDest(ccp dest,bool mkdir)3709 void SetDest ( ccp dest, bool mkdir )
3710 {
3711 #ifdef __CYGWIN__
3712 opt_dest = IsWindowsDriveSpec(dest)
3713 ? AllocNormalizedFilenameCygwin(dest)
3714 : dest;
3715 #else
3716 opt_dest = dest;
3717 #endif
3718 opt_mkdir = mkdir;
3719 }
3720
3721 //
3722 ///////////////////////////////////////////////////////////////////////////////
3723 /////////////// cert support ///////////////
3724 ///////////////////////////////////////////////////////////////////////////////
3725
AddCertFile(ccp fname,int unused)3726 int AddCertFile ( ccp fname, int unused )
3727 {
3728 SuperFile_t sf;
3729 InitializeSF(&sf);
3730 enumError err = OpenSF(&sf,fname,true,false);
3731 if (!err)
3732 {
3733 if ( sf.f.ftype & FT_A_ISO )
3734 {
3735 wd_disc_t * disc = OpenDiscSF(&sf,false,false);
3736 if (disc)
3737 {
3738 int ip;
3739 for ( ip = 0; ip < disc->n_part; ip++ )
3740 {
3741 wd_part_t * part = disc->part + ip;
3742 if ( part->is_enabled
3743 && !wd_load_part(part,true,false,true)
3744 && part->cert )
3745 {
3746 cert_append_data(&global_cert,part->cert,part->ph.cert_size,true);
3747 }
3748 }
3749 }
3750 }
3751 // [[2do]] [[ft-id]]
3752 else if ( sf.f.ftype & (FT_ID_CERT_BIN|FT_ID_TIK_BIN|FT_ID_TMD_BIN) )
3753 {
3754 const size_t load_size = sf.file_size < sizeof(iobuf)
3755 ? sf.file_size : sizeof(iobuf);
3756 err = ReadSF(&sf,0,iobuf,load_size);
3757 if (!err)
3758 {
3759 size_t skip = 0;
3760 if ( sf.f.ftype & FT_ID_TIK_BIN )
3761 skip = sizeof(wd_ticket_t);
3762 else if ( sf.f.ftype & FT_ID_TMD_BIN )
3763 {
3764 wd_tmd_t * tmd = (wd_tmd_t*)iobuf;
3765 skip = sizeof(wd_tmd_t)
3766 + ntohs(tmd->n_content) * sizeof(wd_tmd_content_t);
3767 }
3768
3769 if ( skip < load_size )
3770 cert_append_data(&global_cert,iobuf+skip,load_size-skip,true);
3771 }
3772 }
3773 }
3774
3775 ResetSF(&sf,0);
3776 return err;
3777 }
3778
3779 //
3780 ///////////////////////////////////////////////////////////////////////////////
3781 /////////////// END ///////////////
3782 ///////////////////////////////////////////////////////////////////////////////
3783
3784