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