static INT_PTR CDECL list_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) { WCHAR *nameW; switch (fdint) { case fdintCABINET_INFO: return 0; case fdintCOPY_FILE: nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 ); if (match_files( nameW )) { char *nameU = strdupWtoA( CP_UNIXCP, nameW ); if (opt_verbose) { char attrs[] = "rxash"; if (!(pfdin->attribs & _A_RDONLY)) attrs[0] = '-'; if (!(pfdin->attribs & _A_EXEC)) attrs[1] = '-'; if (!(pfdin->attribs & _A_ARCH)) attrs[2] = '-'; if (!(pfdin->attribs & _A_SYSTEM)) attrs[3] = '-'; if (!(pfdin->attribs & _A_HIDDEN)) attrs[4] = '-'; printf( " %s %9u %04u/%02u/%02u %02u:%02u:%02u ", attrs, pfdin->cb, (pfdin->date >> 9) + 1980, (pfdin->date >> 5) & 0x0f, pfdin->date & 0x1f, pfdin->time >> 11, (pfdin->time >> 5) & 0x3f, (pfdin->time & 0x1f) * 2 ); } printf( "%s\n", nameU ); cab_free( nameU ); } cab_free( nameW ); return 0; default: WINE_FIXME( "Unexpected notification type %d.\n", fdint ); return 0; } }
static INT_PTR CDECL fci_get_open_info( char *name, USHORT *date, USHORT *time, USHORT *attribs, int *err, void *ptr ) { HANDLE handle; BY_HANDLE_FILE_INFORMATION info; WCHAR *p, *nameW = strdupAtoW( CP_UTF8, name ); handle = CreateFileW( nameW, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if (handle == INVALID_HANDLE_VALUE) { *err = GetLastError(); WINE_ERR( "failed to open %s: error %u\n", wine_dbgstr_w(nameW), *err ); cab_free( nameW ); return -1; } if (!GetFileInformationByHandle( handle, &info )) { *err = GetLastError(); CloseHandle( handle ); cab_free( nameW ); return -1; } FileTimeToDosDateTime( &info.ftLastWriteTime, date, time ); *attribs = info.dwFileAttributes & (_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); for (p = nameW; *p; p++) if (*p >= 0x80) break; if (*p) *attribs |= _A_NAME_IS_UTF; cab_free( nameW ); return (INT_PTR)handle; }
static BOOL add_directory( HFCI fci, WCHAR *dir ) { static const WCHAR wildcardW[] = {'*',0}; WCHAR *p, *buffer; HANDLE handle; WIN32_FIND_DATAW data; BOOL ret = TRUE; if (!(buffer = cab_alloc( (strlenW(dir) + MAX_PATH + 2) * sizeof(WCHAR) ))) return FALSE; strcpyW( buffer, dir ); p = buffer + strlenW( buffer ); if (p > buffer && p[-1] != '\\') *p++ = '\\'; strcpyW( p, wildcardW ); if ((handle = FindFirstFileW( buffer, &data )) != INVALID_HANDLE_VALUE) { do { if (data.cFileName[0] == '.' && !data.cFileName[1]) continue; if (data.cFileName[0] == '.' && data.cFileName[1] == '.' && !data.cFileName[2]) continue; if (data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) continue; strcpyW( p, data.cFileName ); if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ret = add_directory( fci, buffer ); else ret = add_file( fci, buffer ); if (!ret) break; } while (FindNextFileW( handle, &data )); FindClose( handle ); } cab_free( buffer ); return TRUE; }
/* create directories leading to a given file */ static void create_directories( const WCHAR *name ) { WCHAR *path, *p; /* create the directory/directories */ path = cab_alloc( (strlenW(name) + 1) * sizeof(WCHAR) ); strcpyW(path, name); p = strchrW(path, '\\'); while (p != NULL) { *p = 0; if (!CreateDirectoryW( path, NULL )) WINE_TRACE("Couldn't create directory %s - error: %d\n", wine_dbgstr_w(path), GetLastError()); *p = '\\'; p = strchrW(p+1, '\\'); } cab_free( path ); }
static BOOL add_file( HFCI fci, WCHAR *name ) { BOOL ret; char *filename, *path = strdupWtoA( CP_UTF8, name ); if (!opt_preserve_paths) { if ((filename = strrchr( path, '\\' ))) filename++; else filename = path; } else { filename = path; while (*filename == '\\') filename++; /* remove leading backslashes */ } ret = FCIAddFile( fci, path, filename, FALSE, fci_get_next_cab, fci_status, fci_get_open_info, opt_compression ); cab_free( path ); return ret; }
int cab_open(int fd, off_t offset, struct cab_archive *cab) { unsigned int i, folders = 0; struct cab_file *file, *lfile = NULL; struct cab_folder *folder, *lfolder = NULL; struct cab_hdr hdr; struct cab_hdr_opt hdr_opt; struct cab_folder_hdr folder_hdr; struct cab_file_hdr file_hdr; struct stat sb; uint16_t fidx; char *pt; int ret; off_t resfold = 0, rsize; if(lseek(fd, offset, SEEK_SET) == -1) { cli_errmsg("cab_open: Can't lseek to %u (offset)\n", (unsigned int) offset); return CL_ESEEK; } if(cli_readn(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) { cli_dbgmsg("cab_open: Can't read cabinet header\n"); return CL_EFORMAT; /* most likely a corrupted file */ } if(EC32(hdr.signature) != 0x4643534d) { cli_dbgmsg("cab_open: Incorrect CAB signature\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: -------------- Cabinet file ----------------\n"); } if(fstat(fd, &sb) == -1) { cli_errmsg("cab_open: Can't fstat descriptor %d\n", fd); return CL_ESTAT; } rsize = sb.st_size; memset(cab, 0, sizeof(struct cab_archive)); cab->length = EC32(hdr.cbCabinet); cli_dbgmsg("CAB: Cabinet length: %u\n", cab->length); if((off_t) cab->length > rsize) { cli_dbgmsg("CAB: Truncating file size from %lu to %lu\n", (unsigned long int) cab->length, (unsigned long int) rsize); cab->length = (uint32_t) rsize; } cab->nfolders = EC16(hdr.cFolders); if(!cab->nfolders) { cli_dbgmsg("cab_open: No folders in cabinet (fake cab?)\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: Folders: %u\n", cab->nfolders); if(cab->nfolders > CAB_FOLDER_LIMIT) { cab->nfolders = CAB_FOLDER_LIMIT; cli_dbgmsg("CAB: *** Number of folders limited to %u ***\n", cab->nfolders); } } cab->nfiles = EC16(hdr.cFiles); if(!cab->nfiles) { cli_dbgmsg("cab_open: No files in cabinet (fake cab?)\n"); return CL_EFORMAT; } else { cli_dbgmsg("CAB: Files: %u\n", cab->nfiles); if(cab->nfiles > CAB_FILE_LIMIT) { cab->nfiles = CAB_FILE_LIMIT; cli_dbgmsg("CAB: *** Number of files limited to %u ***\n", cab->nfiles); } } cli_dbgmsg("CAB: File format version: %u.%u\n", hdr.versionMajor, hdr.versionMinor); cab->flags = EC16(hdr.flags); if(cab->flags & 0x0004) { if(cli_readn(fd, &hdr_opt, sizeof(hdr_opt)) != sizeof(hdr_opt)) { cli_dbgmsg("cab_open: Can't read file header (fake cab?)\n"); return CL_EFORMAT; /* most likely a corrupted file */ } cab->reshdr = EC16(hdr_opt.cbCFHeader); resfold = hdr_opt.cbCFFolder; cab->resdata = hdr_opt.cbCFData; if(cab->reshdr) { if(lseek(fd, cab->reshdr, SEEK_CUR) == -1) { cli_dbgmsg("cab_open: Can't lseek to %u (fake cab?)\n", cab->reshdr); return CL_EFORMAT; /* most likely a corrupted file */ } } } if(cab->flags & 0x0001) { /* preceeding cabinet */ /* name */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid name of preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet name: %s\n", pt); free(pt); /* info */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid info for preceeding cabinet\n"); else cli_dbgmsg("CAB: Preceeding cabinet info: %s\n", pt); free(pt); } if(cab->flags & 0x0002) { /* next cabinet */ /* name */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid name of next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet name: %s\n", pt); free(pt); /* info */ pt = cab_readstr(fd, &ret); if(ret) return ret; if(cab_chkname(pt, 0)) cli_dbgmsg("CAB: Invalid info for next cabinet\n"); else cli_dbgmsg("CAB: Next cabinet info: %s\n", pt); free(pt); } /* folders */ for(i = 0; i < cab->nfolders; i++) { if(cli_readn(fd, &folder_hdr, sizeof(folder_hdr)) != sizeof(folder_hdr)) { cli_dbgmsg("cab_open: Can't read header for folder %u\n", i); break; } if(resfold) { if(lseek(fd, resfold, SEEK_CUR) == -1) { cli_dbgmsg("cab_open: Can't lseek to %u (resfold)\n", (unsigned int) resfold); break; } } if(EC32(folder_hdr.coffCabStart) + offset > rsize) { cli_dbgmsg("CAB: Folder out of file\n"); continue; } if((EC16(folder_hdr.typeCompress) & 0x000f) > 3) { cli_dbgmsg("CAB: Unknown compression method\n"); continue; } folder = (struct cab_folder *) cli_calloc(1, sizeof(struct cab_folder)); if(!folder) { cli_errmsg("cab_open: Can't allocate memory for folder\n"); cab_free(cab); return CL_EMEM; } folder->cab = (struct cab_archive *) cab; folder->offset = (off_t) EC32(folder_hdr.coffCabStart) + offset; folder->nblocks = EC16(folder_hdr.cCFData); folder->cmethod = EC16(folder_hdr.typeCompress); cli_dbgmsg("CAB: Folder record %u\n", i); cli_dbgmsg("CAB: Folder offset: %u\n", (unsigned int) folder->offset); cli_dbgmsg("CAB: Folder compression method: %d\n", folder->cmethod); if(!lfolder) cab->folders = folder; else lfolder->next = folder; lfolder = folder; folders++; } cli_dbgmsg("CAB: Recorded folders: %u\n", folders); /* files */ if(cab->nfolders != folders && lseek(fd, EC16(hdr.coffFiles), SEEK_SET) == -1) { cli_dbgmsg("cab_open: Can't lseek to hdr.coffFiles\n"); cab_free(cab); return CL_EFORMAT; } for(i = 0; i < cab->nfiles; i++) { if(cli_readn(fd, &file_hdr, sizeof(file_hdr)) != sizeof(file_hdr)) { cli_dbgmsg("cab_open: Can't read file %u header\n", i); break; } file = (struct cab_file *) cli_calloc(1, sizeof(struct cab_file)); if(!file) { cli_errmsg("cab_open: Can't allocate memory for file\n"); cab_free(cab); return CL_EMEM; } file->cab = cab; file->fd = fd; file->offset = EC32(file_hdr.uoffFolderStart); file->length = EC32(file_hdr.cbFile); file->attribs = EC32(file_hdr.attribs); fidx = EC32(file_hdr.iFolder); file->error = CL_SUCCESS; file->name = cab_readstr(fd, &ret); if(ret) { free(file); continue; } cab_chkname(file->name, 1); cli_dbgmsg("CAB: File record %u\n", i); cli_dbgmsg("CAB: File name: %s\n", file->name); cli_dbgmsg("CAB: File offset: %u\n", (unsigned int) file->offset); cli_dbgmsg("CAB: File folder index: %u\n", fidx); cli_dbgmsg("CAB: File attribs: 0x%x\n", file->attribs); if(file->attribs & 0x01) cli_dbgmsg("CAB: * file is read-only\n"); if(file->attribs & 0x02) cli_dbgmsg("CAB: * file is hidden\n"); if(file->attribs & 0x04) cli_dbgmsg("CAB: * file is a system file\n"); if(file->attribs & 0x20) cli_dbgmsg("CAB: * file modified since last backup\n"); if(file->attribs & 0x40) cli_dbgmsg("CAB: * file to be run after extraction\n"); if(file->attribs & 0x80) cli_dbgmsg("CAB: * file name contains UTF\n"); /* folder index */ if(fidx < 0xfffd) { if(fidx > cab->nfolders) { cli_dbgmsg("cab_open: File %s is not associated with any folder\n", file->name); free(file->name); free(file); continue; } file->folder = cab->folders; while(file->folder && fidx--) file->folder = file->folder->next; if(!file->folder) { cli_dbgmsg("cab_open: Folder not found for file %s\n", file->name); free(file->name); free(file); continue; } } else { cli_dbgmsg("CAB: File is split *skipping*\n"); free(file->name); free(file); continue; } if(!lfile) cab->files = file; else lfile->next = file; lfile = file; } return CL_SUCCESS; }
static INT_PTR CDECL extract_notify( FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin ) { WCHAR *file, *nameW, *path = NULL; INT_PTR ret; switch (fdint) { case fdintCABINET_INFO: return 0; case fdintCOPY_FILE: nameW = strdupAtoW( (pfdin->attribs & _A_NAME_IS_UTF) ? CP_UTF8 : CP_ACP, pfdin->psz1 ); if (opt_preserve_paths) { file = nameW; while (*file == '\\') file++; /* remove leading backslashes */ } else { if ((file = strrchrW( nameW, '\\' ))) file++; else file = nameW; } if (opt_dest_dir) { path = cab_alloc( (strlenW(opt_dest_dir) + strlenW(file) + 1) * sizeof(WCHAR) ); strcpyW( path, opt_dest_dir ); strcatW( path, file ); } else path = file; if (match_files( file )) { if (opt_verbose) { char *nameU = strdupWtoA( CP_UNIXCP, path ); printf( "extracting %s\n", nameU ); cab_free( nameU ); } create_directories( path ); /* FIXME: check for existing file and overwrite mode */ ret = (INT_PTR)CreateFileW( path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); } else ret = 0; cab_free( nameW ); if (path != file) cab_free( path ); return ret; case fdintCLOSE_FILE_INFO: CloseHandle( (HANDLE)pfdin->hf ); return 0; case fdintNEXT_CABINET: WINE_TRACE("Next cab: status %u, path '%s', file '%s'\n", pfdin->fdie, pfdin->psz3, pfdin->psz1); return pfdin->fdie == FDIERROR_NONE ? 0 : -1; case fdintENUMERATE: return 0; default: WINE_FIXME( "Unexpected notification type %d.\n", fdint ); return 0; } }