1 /******************************* LICENCE **************************************
2 * Any code in this file may be redistributed or modified under the terms of
3 * the GNU General Public Licence as published by the Free Software
4 * Foundation; version 2 of the licence.
5 ****************************** END LICENCE ***********************************/
6
7 /******************************************************************************
8 * Author:
9 * Andrew Smith, http://littlesvr.ca/misc/contactandrew.php
10 *
11 * Contributors:
12 *
13 ******************************************************************************/
14
15 #include <stdlib.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <sys/stat.h>
19 #include <stdio.h>
20
21 #include "bk.h"
22 #include "bkInternal.h"
23 #include "bkExtract.h"
24 #include "bkPath.h"
25 #include "bkError.h"
26 #include "bkMisc.h"
27 #include "bkIoWrappers.h"
28
29 /*******************************************************************************
30 * bk_extract_boot_record()
31 * Extracts the el torito boot record to the file destPathAndName, with
32 * permissions destFilePerms.
33 * */
bk_extract_boot_record(VolInfo * volInfo,const char * destPathAndName,unsigned destFilePerms)34 int bk_extract_boot_record(VolInfo* volInfo, const char* destPathAndName,
35 unsigned destFilePerms)
36 {
37 int srcFile; /* returned by open() */
38 bool srcFileWasOpened;
39 int destFile; /* returned by open() */
40 int rc;
41
42 if(volInfo->bootMediaType == BOOT_MEDIA_NONE)
43 return BKERROR_EXTRACT_ABSENT_BOOT_RECORD;
44
45 if(volInfo->bootMediaType != BOOT_MEDIA_NO_EMULATION &&
46 volInfo->bootMediaType != BOOT_MEDIA_1_2_FLOPPY &&
47 volInfo->bootMediaType != BOOT_MEDIA_1_44_FLOPPY &&
48 volInfo->bootMediaType != BOOT_MEDIA_2_88_FLOPPY)
49 {
50 return BKERROR_EXTRACT_UNKNOWN_BOOT_MEDIA;
51 }
52
53 /* SET source file (open if needed) */
54 if(volInfo->bootRecordIsVisible)
55 /* boot record is a file in the tree */
56 {
57 if(volInfo->bootRecordOnImage->onImage)
58 {
59 srcFile = volInfo->imageForReading;
60 readSeekSet(volInfo, volInfo->bootRecordOnImage->position, SEEK_SET);
61 srcFileWasOpened = false;
62 }
63 else
64 {
65 srcFile = open(volInfo->bootRecordOnImage->pathAndName, O_RDONLY, 0);
66 if(srcFile == -1)
67 return BKERROR_OPEN_READ_FAILED;
68 srcFileWasOpened = true;
69 }
70 }
71 else
72 /* boot record is not a file in the tree */
73 {
74 if(volInfo->bootRecordIsOnImage)
75 {
76 srcFile = volInfo->imageForReading;
77 readSeekSet(volInfo, volInfo->bootRecordOffset, SEEK_SET);
78 srcFileWasOpened = false;
79 }
80 else
81 {
82 srcFile = open(volInfo->bootRecordPathAndName, O_RDONLY, 0);
83 if(srcFile == -1)
84 return BKERROR_OPEN_READ_FAILED;
85 srcFileWasOpened = true;
86 }
87 }
88 /* END SET source file (open if needed) */
89
90 destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC,
91 destFilePerms);
92 if(destFile == -1)
93 {
94 if(srcFileWasOpened)
95 bkClose(srcFile);
96 return BKERROR_OPEN_WRITE_FAILED;
97 }
98
99 rc = copyByteBlock(volInfo, srcFile, destFile, volInfo->bootRecordSize);
100
101 bkClose(destFile);
102 if(srcFileWasOpened)
103 bkClose(srcFile);
104
105 if(rc <= 0)
106 return rc;
107
108 return 1;
109 }
110
bk_extract(VolInfo * volInfo,const char * srcPathAndName,const char * destDir,bool keepPermissions,void (* progressFunction)(VolInfo *))111 int bk_extract(VolInfo* volInfo, const char* srcPathAndName,
112 const char* destDir, bool keepPermissions,
113 void(*progressFunction)(VolInfo*))
114 {
115 return bk_extract_as(volInfo, srcPathAndName, destDir, NULL,
116 keepPermissions, progressFunction);
117 }
118
bk_extract_as(VolInfo * volInfo,const char * srcPathAndName,const char * destDir,const char * nameToUse,bool keepPermissions,void (* progressFunction)(VolInfo *))119 int bk_extract_as(VolInfo* volInfo, const char* srcPathAndName,
120 const char* destDir, const char* nameToUse,
121 bool keepPermissions, void(*progressFunction)(VolInfo*))
122 {
123 int rc;
124 NewPath srcPath;
125 BkDir* parentDir;
126 bool dirFound;
127
128 volInfo->progressFunction = progressFunction;
129 volInfo->stopOperation = false;
130
131 rc = makeNewPathFromString(srcPathAndName, &srcPath);
132 if(rc <= 0)
133 {
134 freePathContents(&srcPath);
135 return rc;
136 }
137
138 if(srcPath.numChildren == 0)
139 {
140 freePathContents(&srcPath);
141 return BKERROR_EXTRACT_ROOT;
142 }
143
144 /* i want the parent directory */
145 srcPath.numChildren--;
146 dirFound = findDirByNewPath(&srcPath, &(volInfo->dirTree), &parentDir);
147 srcPath.numChildren++;
148 if(!dirFound)
149 {
150 freePathContents(&srcPath);
151 return BKERROR_DIR_NOT_FOUND_ON_IMAGE;
152 }
153
154 rc = extract(volInfo, parentDir, srcPath.children[srcPath.numChildren - 1],
155 destDir, nameToUse, keepPermissions);
156
157 freePathContents(&srcPath);
158
159 if(rc <= 0)
160 {
161 return rc;
162 }
163
164 return 1;
165 }
166
copyByteBlock(VolInfo * volInfo,int src,int dest,unsigned numBytes)167 int copyByteBlock(VolInfo* volInfo, int src, int dest, unsigned numBytes)
168 {
169 int rc;
170 int count;
171 int numBlocks;
172 int sizeLastBlock;
173
174 numBlocks = numBytes / READ_WRITE_BUFFER_SIZE;
175 sizeLastBlock = numBytes % READ_WRITE_BUFFER_SIZE;
176
177 maybeUpdateProgress(volInfo);
178 if(volInfo->stopOperation)
179 return BKERROR_OPER_CANCELED_BY_USER;
180
181 for(count = 0; count < numBlocks; count++)
182 {
183 maybeUpdateProgress(volInfo);
184 if(volInfo->stopOperation)
185 return BKERROR_OPER_CANCELED_BY_USER;
186
187 rc = bkRead(src, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE);
188 if(rc != READ_WRITE_BUFFER_SIZE)
189 return BKERROR_READ_GENERIC;
190 rc = bkWrite(dest, volInfo->readWriteBuffer, READ_WRITE_BUFFER_SIZE);
191 if(rc == -1)
192 return BKERROR_WRITE_GENERIC;
193 }
194
195 if(sizeLastBlock > 0)
196 {
197 rc = bkRead(src, volInfo->readWriteBuffer, sizeLastBlock);
198 if(rc != sizeLastBlock)
199 return BKERROR_READ_GENERIC;
200 rc = bkWrite(dest, volInfo->readWriteBuffer, sizeLastBlock);
201 if(rc == -1)
202 return BKERROR_WRITE_GENERIC;
203 }
204
205 return 1;
206 }
207
existsOnFs(const char * pathAndName)208 bool existsOnFs(const char* pathAndName)
209 {
210 struct stat statStruct;
211
212 if(lstat(pathAndName, &statStruct) == 0)
213 return true;
214 else
215 return false;
216 }
217
extract(VolInfo * volInfo,BkDir * parentDir,char * nameToExtract,const char * destDir,const char * nameToUse,bool keepPermissions)218 int extract(VolInfo* volInfo, BkDir* parentDir, char* nameToExtract,
219 const char* destDir, const char* nameToUse, bool keepPermissions)
220 {
221 BkFileBase* child;
222 int rc;
223
224 child = parentDir->children;
225 while(child != NULL)
226 {
227 if(volInfo->stopOperation)
228 return BKERROR_OPER_CANCELED_BY_USER;
229
230 if(strcmp(child->name, nameToExtract) == 0)
231 {
232 if( IS_DIR(child->posixFileMode) )
233 {
234 rc = extractDir(volInfo, BK_DIR_PTR(child), destDir,
235 nameToUse, keepPermissions);
236 }
237 else if ( IS_REG_FILE(child->posixFileMode) )
238 {
239 rc = extractFile(volInfo, BK_FILE_PTR(child), destDir,
240 nameToUse, keepPermissions);
241 }
242 else if ( IS_SYMLINK(child->posixFileMode) )
243 {
244 rc = extractSymlink(BK_SYMLINK_PTR(child), destDir,
245 nameToUse);
246 }
247 else
248 {
249 rc = 1;
250 printf("trying to extract something that's not a file, "
251 "symlink or directory, ignored\n");fflush(NULL);
252 }
253
254 if(rc <= 0)
255 {
256 bool goOn;
257
258 if(volInfo->warningCbk != NULL && !volInfo->stopOperation)
259 /* perhaps the user wants to ignore this failure */
260 {
261 snprintf(volInfo->warningMessage, BK_WARNING_MAX_LEN,
262 "Failed to extract item '%s': '%s'",
263 child->name,
264 bk_get_error_string(rc));
265 goOn = volInfo->warningCbk(volInfo->warningMessage);
266 rc = BKWARNING_OPER_PARTLY_FAILED;
267 }
268 else
269 goOn = false;
270
271 if(!goOn)
272 {
273 volInfo->stopOperation = true;
274 return rc;
275 }
276 }
277 }
278
279 child = child->next;
280 }
281
282 return 1;
283 }
284
extractDir(VolInfo * volInfo,BkDir * srcDir,const char * destDir,const char * nameToUse,bool keepPermissions)285 int extractDir(VolInfo* volInfo, BkDir* srcDir, const char* destDir,
286 const char* nameToUse, bool keepPermissions)
287 {
288 int rc;
289 BkFileBase* child;
290
291 /* vars to create destination dir */
292 char* newDestDir;
293 unsigned destDirPerms;
294
295 /* CREATE destination dir on filesystem */
296 /* 1 for '\0' */
297 if(nameToUse == NULL)
298 newDestDir = malloc(strlen(destDir) + strlen(BK_BASE_PTR(srcDir)->name) + 2);
299 else
300 newDestDir = malloc(strlen(destDir) + strlen(nameToUse) + 2);
301 if(newDestDir == NULL)
302 return BKERROR_OUT_OF_MEMORY;
303
304 strcpy(newDestDir, destDir);
305 if(destDir[strlen(destDir) - 1] != '/')
306 strcat(newDestDir, "/");
307
308 if(nameToUse == NULL)
309 strcat(newDestDir, BK_BASE_PTR(srcDir)->name);
310 else
311 strcat(newDestDir, nameToUse);
312
313 if(keepPermissions)
314 destDirPerms = BK_BASE_PTR(BK_BASE_PTR(srcDir))->posixFileMode;
315 else
316 destDirPerms = volInfo->posixDirDefaults;
317 /* want to make sure user has write and execute permissions to new directory
318 * so that can extract stuff into it */
319 destDirPerms |= 0300;
320
321 if(existsOnFs(newDestDir))
322 {
323 free(newDestDir);
324 return BKERROR_DUPLICATE_EXTRACT;
325 }
326
327 rc = mkdir(newDestDir, destDirPerms);
328 if(rc == -1)
329 {
330 free(newDestDir);
331 return BKERROR_MKDIR_FAILED;
332 }
333 /* END CREATE destination dir on filesystem */
334
335 /* EXTRACT children */
336 child = srcDir->children;
337 while(child != NULL)
338 {
339 rc = extract(volInfo, srcDir, child->name, newDestDir,
340 NULL, keepPermissions);
341 if(rc <= 0)
342 {
343 free(newDestDir);
344 return rc;
345 }
346
347 child = child->next;
348 }
349 /* END EXTRACT children */
350
351 free(newDestDir);
352
353 return 1;
354 }
355
extractFile(VolInfo * volInfo,BkFile * srcFileInTree,const char * destDir,const char * nameToUse,bool keepPermissions)356 int extractFile(VolInfo* volInfo, BkFile* srcFileInTree, const char* destDir,
357 const char* nameToUse, bool keepPermissions)
358 {
359 int srcFile;
360 bool srcFileWasOpened;
361 char* destPathAndName;
362 unsigned destFilePerms;
363 int destFile; /* returned by open() */
364 int rc;
365 BkStatStruct statStruct;
366
367 if(srcFileInTree->onImage)
368 {
369 srcFile = volInfo->imageForReading;
370 bkSeekSet(volInfo->imageForReading, srcFileInTree->position, SEEK_SET);
371 srcFileWasOpened = false;
372 }
373 else
374 {
375 srcFile = open(srcFileInTree->pathAndName, O_RDONLY, 0);
376 if(srcFile == -1)
377 return BKERROR_OPEN_READ_FAILED;
378 srcFileWasOpened = true;
379
380 /* UPDATE the file's size, in case it's changed since we added it */
381 rc = bkStat(srcFileInTree->pathAndName, &statStruct);
382 if(rc != 0)
383 return BKERROR_STAT_FAILED;
384
385 if(statStruct.st_size > 0xFFFFFFFF)
386 /* size won't fit in a 32bit variable on the iso */
387 return BKERROR_EDITED_EXTRACT_TOO_BIG;
388
389 srcFileInTree->size = statStruct.st_size;
390 /* UPDATE the file's size, in case it's changed since we added it */
391 }
392
393 if(nameToUse == NULL)
394 destPathAndName = malloc(strlen(destDir) +
395 strlen(BK_BASE_PTR(srcFileInTree)->name) + 2);
396 else
397 destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2);
398 if(destPathAndName == NULL)
399 {
400 if(srcFileWasOpened)
401 bkClose(srcFile);
402 return BKERROR_OUT_OF_MEMORY;
403 }
404
405 strcpy(destPathAndName, destDir);
406 if(destDir[strlen(destDir) - 1] != '/')
407 strcat(destPathAndName, "/");
408 if(nameToUse == NULL)
409 strcat(destPathAndName, BK_BASE_PTR(srcFileInTree)->name);
410 else
411 strcat(destPathAndName, nameToUse);
412
413 if(existsOnFs(destPathAndName))
414 {
415 if(srcFileWasOpened)
416 bkClose(srcFile);
417 free(destPathAndName);
418 return BKERROR_DUPLICATE_EXTRACT;
419 }
420
421 /* WRITE file */
422 if(keepPermissions)
423 destFilePerms = BK_BASE_PTR(srcFileInTree)->posixFileMode;
424 else
425 destFilePerms = volInfo->posixFileDefaults;
426
427 destFile = open(destPathAndName, O_WRONLY | O_CREAT | O_TRUNC, destFilePerms);
428 if(destFile == -1)
429 {
430 if(srcFileWasOpened)
431 bkClose(srcFile);
432 free(destPathAndName);
433 return BKERROR_OPEN_WRITE_FAILED;
434 }
435
436 free(destPathAndName);
437
438 rc = copyByteBlock(volInfo, srcFile, destFile, srcFileInTree->size);
439 if(rc < 0)
440 {
441 bkClose(destFile);
442 if(srcFileWasOpened)
443 bkClose(srcFile);
444 return rc;
445 }
446
447 bkClose(destFile);
448 if(destFile == -1)
449 {
450 if(srcFileWasOpened)
451 bkClose(srcFile);
452 return BKERROR_EXOTIC;
453 }
454 /* END WRITE file */
455
456 if(srcFileWasOpened)
457 bkClose(srcFile);
458
459 return 1;
460 }
461
extractSymlink(BkSymLink * srcLink,const char * destDir,const char * nameToUse)462 int extractSymlink(BkSymLink* srcLink, const char* destDir,
463 const char* nameToUse)
464 {
465 char* destPathAndName;
466 int rc;
467
468 if(nameToUse == NULL)
469 destPathAndName = malloc(strlen(destDir) +
470 strlen(BK_BASE_PTR(srcLink)->name) + 2);
471 else
472 destPathAndName = malloc(strlen(destDir) + strlen(nameToUse) + 2);
473 if(destPathAndName == NULL)
474 return BKERROR_OUT_OF_MEMORY;
475
476 strcpy(destPathAndName, destDir);
477 if(destDir[strlen(destDir) - 1] != '/')
478 strcat(destPathAndName, "/");
479 if(nameToUse == NULL)
480 strcat(destPathAndName, BK_BASE_PTR(srcLink)->name);
481 else
482 strcat(destPathAndName, nameToUse);
483
484 if(existsOnFs(destPathAndName))
485 {
486 free(destPathAndName);
487 return BKERROR_DUPLICATE_EXTRACT;
488 }
489
490 rc = symlink(srcLink->target, destPathAndName);
491 if(rc == -1)
492 {
493 free(destPathAndName);
494 return BKERROR_CREATE_SYMLINK_FAILED;
495 }
496
497 free(destPathAndName);
498
499 return 1;
500 }
501