From f02fbe3acc323eff4e359e5f68ebbe59ff50119d Mon Sep 17 00:00:00 2001 From: Pete Batard Date: Thu, 4 Apr 2019 19:12:48 +0100 Subject: [PATCH] [core] add full extraction support for efi.img * Also, Solus distro maintainers, I hate you! --- .vs/syslinux-libfat.vcxproj | 1 + .vs/syslinux-libfat.vcxproj.filters | 3 + src/iso.c | 206 +++++++++++++++++++--------- src/msapi_utf8.h | 14 +- src/process.h | 2 +- src/rufus.c | 4 + src/rufus.h | 3 +- src/rufus.rc | 10 +- src/syslinux/libfat/Makefile.am | 2 +- src/syslinux/libfat/Makefile.in | 10 +- src/syslinux/libfat/cache.c | 1 - src/syslinux/libfat/dumpdir.c | 126 +++++++++++++++++ src/syslinux/libfat/libfat.h | 22 +++ 13 files changed, 327 insertions(+), 77 deletions(-) create mode 100644 src/syslinux/libfat/dumpdir.c diff --git a/.vs/syslinux-libfat.vcxproj b/.vs/syslinux-libfat.vcxproj index 6b8714ef..246a0240 100644 --- a/.vs/syslinux-libfat.vcxproj +++ b/.vs/syslinux-libfat.vcxproj @@ -42,6 +42,7 @@ + diff --git a/.vs/syslinux-libfat.vcxproj.filters b/.vs/syslinux-libfat.vcxproj.filters index 717f809c..514c11eb 100644 --- a/.vs/syslinux-libfat.vcxproj.filters +++ b/.vs/syslinux-libfat.vcxproj.filters @@ -37,5 +37,8 @@ Source Files + + Source Files + \ No newline at end of file diff --git a/src/iso.c b/src/iso.c index 3e49943c..5a336787 100644 --- a/src/iso.c +++ b/src/iso.c @@ -59,6 +59,7 @@ uint32_t GetInstallWimVersion(const char* iso); typedef struct { BOOLEAN is_cfg; + BOOLEAN is_conf; BOOLEAN is_syslinux_cfg; BOOLEAN is_grub_cfg; BOOLEAN is_old_c32[NB_OLD_C32]; @@ -291,11 +292,11 @@ static void fix_config(const char* psz_fullpath, const char* psz_path, const cha // Workaround for config files requiring an ISO label for kernel append that may be // different from our USB label. Oh, and these labels must have spaces converted to \x20. - if (props->is_cfg) { + if ((props->is_cfg) || (props->is_conf)) { iso_label = replace_char(img_report.label, ' ', "\\x20"); usb_label = replace_char(img_report.usb_label, ' ', "\\x20"); if ((iso_label != NULL) && (usb_label != NULL)) { - if (replace_in_token_data(src, (props->is_grub_cfg) ? "linuxefi" : "append", + if (replace_in_token_data(src, (props->is_grub_cfg) ? "linuxefi" : ((props->is_conf) ? "options" : "append"), iso_label, usb_label, TRUE) != NULL) uprintf(" Patched %s: '%s' ➔ '%s'\n", src, iso_label, usb_label); } @@ -881,7 +882,7 @@ out: img_report.sl_version_str); } } - if (!IS_EFI_BOOTABLE(img_report) && HAS_EFI_IMG(img_report) && ExtractEfiImgFiles(NULL)) { + if (!IS_EFI_BOOTABLE(img_report) && HAS_EFI_IMG(img_report) && HasEfiImgBootLoaders()) { img_report.has_efi = 0x80; } if (HAS_WINPE(img_report)) { @@ -932,9 +933,9 @@ out: StrArrayDestroy(&isolinux_path); SendMessage(hMainDialog, UM_PROGRESS_EXIT, 0, 0); } else { - // For Debian live ISOs, that only provide EFI boot files in a FAT efi.img + // Solus and other ISOs only provide EFI boot files in a FAT efi.img if (img_report.has_efi == 0x80) - ExtractEfiImgFiles(dest_dir); + DumpFatDir(dest_dir, 0); if (HAS_SYSLINUX(img_report)) { static_sprintf(path, "%s\\syslinux.cfg", dest_dir); // Create a /syslinux.cfg (if none exists) that points to the existing isolinux cfg @@ -1187,26 +1188,19 @@ int iso9660_readfat(intptr_t pp, void *buf, size_t secsize, libfat_sector_t sec) } /* - * Extract EFI bootloaders files from an ISO-9660 FAT img file into directory . - * If is NULL, returns TRUE if an EFI bootloader exists in the img. - * If is not NULL, returns TRUE if any if the bootloaders was properly written. + * Returns TRUE if an EFI bootloader exists in the img. */ -BOOL ExtractEfiImgFiles(const char* dir) +BOOL HasEfiImgBootLoaders(void) { BOOL ret = FALSE; - HANDLE handle; - DWORD size, file_size, written; iso9660_t* p_iso = NULL; iso9660_stat_t* p_statbuf = NULL; iso9660_readfat_private* p_private = NULL; - libfat_sector_t s; int32_t dc, c; struct libfat_filesystem *lf_fs = NULL; struct libfat_direntry direntry; char name[12] = { 0 }; - char path[64]; int i, j, k; - void* buf; if ((image_path == NULL) || !HAS_EFI_IMG(img_report)) return FALSE; @@ -1261,57 +1255,10 @@ BOOL ExtractEfiImgFiles(const char* dir) } c = libfat_searchdir(lf_fs, dc, name, &direntry); if (c > 0) { - if (dir == NULL) { - if (!ret) - uprintf(" Detected EFI bootloader(s) (from '%s'):", img_report.efi_img_path); - uprintf(" ● '%s'", efi_bootname[i]); - ret = TRUE; - } else { - file_size = direntry.entry[28] + (direntry.entry[29] << 8) + (direntry.entry[30] << 16) + - (direntry.entry[31] << 24); - // Sanity check - if (file_size > 64 * MB) { - uprintf("Warning: File size is larger than 64 MB => not extracted"); - continue; - } - static_sprintf(path, "%s\\efi", dir); - if (!CreateDirectoryA(path, 0) && (GetLastError() != ERROR_ALREADY_EXISTS)) { - uprintf("Could not create directory '%s': %s\n", path, WindowsErrorString()); - continue; - } - static_strcat(path, "\\boot"); - if (!CreateDirectoryA(path, 0) && (GetLastError() != ERROR_ALREADY_EXISTS)) { - uprintf("Could not create directory '%s': %s\n", path, WindowsErrorString()); - continue; - } - static_strcat(path, "\\"); - static_strcat(path, efi_bootname[i]); - uprintf("Extracting: %s (from '%s', %s)", path, img_report.efi_img_path, - SizeToHumanReadable(file_size, FALSE, FALSE)); - handle = CreateFileA(path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, - NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - if (handle == INVALID_HANDLE_VALUE) { - uprintf("Unable to create '%s': %s", path, WindowsErrorString()); - continue; - } - - written = 0; - s = libfat_clustertosector(lf_fs, c); - while ((s != 0) && (s < 0xFFFFFFFFULL) && (written < file_size)) { - buf = libfat_get_sector(lf_fs, s); - size = MIN(LIBFAT_SECTOR_SIZE, file_size - written); - if (!WriteFileWithRetry(handle, buf, size, &size, WRITE_RETRIES) || - (size != MIN(LIBFAT_SECTOR_SIZE, file_size - written))) { - uprintf("Error writing '%s': %s", path, WindowsErrorString()); - CloseHandle(handle); - continue; - } - written += size; - s = libfat_nextsector(lf_fs, s); - } - CloseHandle(handle); - ret = TRUE; - } + if (!ret) + uprintf(" Detected EFI bootloader(s) (from '%s'):", img_report.efi_img_path); + uprintf(" ● '%s'", efi_bootname[i]); + ret = TRUE; } } @@ -1327,6 +1274,135 @@ out: return ret; } +BOOL DumpFatDir(const char* path, int32_t cluster) +{ + // We don't have concurrent calls to this function, so a static lf_fs is fine + static struct libfat_filesystem *lf_fs = NULL; + void* buf; + char *target = NULL, *name = NULL; + BOOL ret = FALSE; + HANDLE handle; + DWORD size, written; + libfat_diritem_t diritem = { 0 }; + libfat_dirpos_t dirpos = { cluster, -1, 0 }; + libfat_sector_t s; + iso9660_t* p_iso = NULL; + iso9660_stat_t* p_statbuf = NULL; + iso9660_readfat_private* p_private = NULL; + + if (path == NULL) + return -1; + + if (cluster == 0) { + // Root dir => Perform init stuff + if (image_path == NULL) + return FALSE; + p_iso = iso9660_open(image_path); + if (p_iso == NULL) { + uprintf("Could not open image '%s' as an ISO-9660 file system", image_path); + goto out; + } + p_statbuf = iso9660_ifs_stat_translate(p_iso, img_report.efi_img_path); + if (p_statbuf == NULL) { + uprintf("Could not get ISO-9660 file information for file %s\n", img_report.efi_img_path); + goto out; + } + p_private = malloc(sizeof(iso9660_readfat_private)); + if (p_private == NULL) + goto out; + p_private->p_iso = p_iso; + p_private->lsn = p_statbuf->lsn[0]; // Image should be small enough not to use multiextents + p_private->sec_start = 0; + // Populate our intial buffer + if (iso9660_iso_seek_read(p_private->p_iso, p_private->buf, p_private->lsn, ISO_NB_BLOCKS) != ISO_NB_BLOCKS * ISO_BLOCKSIZE) { + uprintf("Error reading ISO-9660 file %s at LSN %lu\n", img_report.efi_img_path, (long unsigned int)p_private->lsn); + goto out; + } + lf_fs = libfat_open(iso9660_readfat, (intptr_t)p_private); + if (lf_fs == NULL) { + uprintf("FAT access error"); + goto out; + } + } + + do { + dirpos.cluster = libfat_dumpdir(lf_fs, &dirpos, &diritem); + if (dirpos.cluster >= 0) { + name = wchar_to_utf8(diritem.name); + target = malloc(strlen(path) + safe_strlen(name) + 2); + if ((name == NULL) || (target == NULL)) { + uprintf("Could not allocate buffer"); + safe_free(name); + goto out; + } + strcpy(target, path); + strcat(target, "\\"); + strcat(target, name); + if (diritem.attributes & 0x10) { + // Directory => Create directory + if (!CreateDirectoryU(target, 0) && (GetLastError() != ERROR_ALREADY_EXISTS)) { + uprintf("Could not create directory '%s': %s\n", target, WindowsErrorString()); + continue; + } + if (!DumpFatDir(target, dirpos.cluster)) + goto out; + } else { + // Need to figure out if it's a .conf file (Damn you Solus!!) + EXTRACT_PROPS props = { 0 }; + size_t len = strlen(name); + props.is_conf = ((len > 4) && (stricmp(&name[len - 5], ".conf") == 0)); + uprintf("Extracting: %s (from '%s', %s)", target, img_report.efi_img_path, + SizeToHumanReadable(diritem.size, FALSE, FALSE)); + handle = CreateFileU(target, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + NULL, CREATE_ALWAYS, diritem.attributes, NULL); + if (handle == INVALID_HANDLE_VALUE) { + uprintf("Unable to create '%s': %s", target, WindowsErrorString()); + continue; + } + + written = 0; + s = libfat_clustertosector(lf_fs, dirpos.cluster); + while ((s != 0) && (s < 0xFFFFFFFFULL) && (written < diritem.size)) { + if (FormatStatus) goto out; + buf = libfat_get_sector(lf_fs, s); + size = MIN(LIBFAT_SECTOR_SIZE, diritem.size - written); + if (!WriteFileWithRetry(handle, buf, size, &size, WRITE_RETRIES) || + (size != MIN(LIBFAT_SECTOR_SIZE, diritem.size - written))) { + uprintf("Error writing '%s': %s", target, WindowsErrorString()); + CloseHandle(handle); + continue; + } + written += size; + s = libfat_nextsector(lf_fs, s); + // Trust me, you *REALLY* want to invoke libfat_flush() here + libfat_flush(lf_fs); + } + CloseHandle(handle); + if (props.is_conf) + fix_config(target, NULL, NULL, &props); + } + safe_free(target); + safe_free(name); + } + } while (dirpos.cluster >= 0); + ret = TRUE; + +out: + if (cluster == 0) { + if (lf_fs != NULL) { + libfat_close(lf_fs); + lf_fs = NULL; + } + if (p_statbuf != NULL) + safe_free(p_statbuf->rr.psz_symlink); + safe_free(p_statbuf); + safe_free(p_private); + if (p_iso != NULL) + iso9660_close(p_iso); + } + return ret; +} + // VirtDisk API Prototypes - Only available for Windows 8 or later PF_TYPE_DECL(WINAPI, DWORD, OpenVirtualDisk, (PVIRTUAL_STORAGE_TYPE, PCWSTR, VIRTUAL_DISK_ACCESS_MASK, OPEN_VIRTUAL_DISK_FLAG, POPEN_VIRTUAL_DISK_PARAMETERS, PHANDLE)); diff --git a/src/msapi_utf8.h b/src/msapi_utf8.h index 617226c9..67c3648d 100644 --- a/src/msapi_utf8.h +++ b/src/msapi_utf8.h @@ -452,7 +452,7 @@ static __inline DWORD CharUpperBuffU(char* lpString, DWORD len) static __inline HANDLE CreateFileU(const char* lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, - DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) + DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { HANDLE ret = INVALID_HANDLE_VALUE; DWORD err = ERROR_INVALID_DATA; @@ -465,6 +465,18 @@ static __inline HANDLE CreateFileU(const char* lpFileName, DWORD dwDesiredAccess return ret; } +static __inline BOOL CreateDirectoryU(const char* lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) +{ + BOOL ret = FALSE; + DWORD err = ERROR_INVALID_DATA; + wconvert(lpPathName); + ret = CreateDirectoryW(wlpPathName, lpSecurityAttributes); + err = GetLastError(); + wfree(lpPathName); + SetLastError(err); + return ret; +} + static __inline BOOL CopyFileU(const char* lpExistingFileName, const char* lpNewFileName, BOOL bFailIfExists) { BOOL ret = FALSE; diff --git a/src/process.h b/src/process.h index fee9f99f..fd506fae 100644 --- a/src/process.h +++ b/src/process.h @@ -121,7 +121,7 @@ typedef struct _OBJECT_TYPES_INFORMATION typedef struct _PROCESS_BASIC_INFORMATION_WOW64 { PVOID Reserved1[2]; - // MinGW32 screws us with a sizeof(PVOID64) of 4 instead of 8 => Use an ULONGLONG instead + // MinGW32 screws us with a sizeof(PVOID64) of 4 instead of 8 => Use ULONGLONG instead ULONGLONG PebBaseAddress; PVOID Reserved2[4]; ULONG_PTR UniqueProcessId[2]; diff --git a/src/rufus.c b/src/rufus.c index 52ddfaa7..7f00cb71 100755 --- a/src/rufus.c +++ b/src/rufus.c @@ -1886,6 +1886,10 @@ static INT_PTR CALLBACK MainCallback(HWND hDlg, UINT message, WPARAM wParam, LPA case WM_COMMAND: #ifdef RUFUS_TEST if (LOWORD(wParam) == IDC_TEST) { + image_path = "C:\\Downloads\\fat.iso"; + strcpy(img_report.efi_img_path, "efi.img"); + DumpFatDir("C:\\tmp", 0); + image_path = NULL; break; } #endif diff --git a/src/rufus.h b/src/rufus.h index 7f767d80..843758b2 100644 --- a/src/rufus.h +++ b/src/rufus.h @@ -503,7 +503,8 @@ extern BOOL ExtractAppIcon(const char* filename, BOOL bSilent); extern BOOL ExtractDOS(const char* path); extern BOOL ExtractISO(const char* src_iso, const char* dest_dir, BOOL scan); extern int64_t ExtractISOFile(const char* iso, const char* iso_file, const char* dest_file, DWORD attributes); -extern BOOL ExtractEfiImgFiles(const char* dir); +extern BOOL HasEfiImgBootLoaders(void); +extern BOOL DumpFatDir(const char* path, int32_t cluster); extern char* MountISO(const char* path); extern void UnMountISO(void); extern BOOL InstallSyslinux(DWORD drive_index, char drive_letter, int fs); diff --git a/src/rufus.rc b/src/rufus.rc index e6cdcf74..88d250c7 100644 --- a/src/rufus.rc +++ b/src/rufus.rc @@ -33,7 +33,7 @@ LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL IDD_DIALOG DIALOGEX 12, 12, 232, 326 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_ACCEPTFILES -CAPTION "Rufus 3.6.1506" +CAPTION "Rufus 3.6.1507" FONT 9, "Segoe UI Symbol", 400, 0, 0x0 BEGIN LTEXT "Drive Properties",IDS_DRIVE_PROPERTIES_TXT,8,6,53,12,NOT WS_GROUP @@ -394,8 +394,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 3,6,1506,0 - PRODUCTVERSION 3,6,1506,0 + FILEVERSION 3,6,1507,0 + PRODUCTVERSION 3,6,1507,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -413,13 +413,13 @@ BEGIN VALUE "Comments", "https://akeo.ie" VALUE "CompanyName", "Akeo Consulting" VALUE "FileDescription", "Rufus" - VALUE "FileVersion", "3.6.1506" + VALUE "FileVersion", "3.6.1507" VALUE "InternalName", "Rufus" VALUE "LegalCopyright", " 2011-2019 Pete Batard (GPL v3)" VALUE "LegalTrademarks", "https://www.gnu.org/copyleft/gpl.html" VALUE "OriginalFilename", "rufus-3.6.exe" VALUE "ProductName", "Rufus" - VALUE "ProductVersion", "3.6.1506" + VALUE "ProductVersion", "3.6.1507" END END BLOCK "VarFileInfo" diff --git a/src/syslinux/libfat/Makefile.am b/src/syslinux/libfat/Makefile.am index d39ccf38..26a31d92 100644 --- a/src/syslinux/libfat/Makefile.am +++ b/src/syslinux/libfat/Makefile.am @@ -1,4 +1,4 @@ noinst_LIBRARIES = libfat.a -libfat_a_SOURCES = cache.c fatchain.c open.c searchdir.c +libfat_a_SOURCES = cache.c fatchain.c open.c searchdir.c dumpdir.c libfat_a_CFLAGS = $(AM_CFLAGS) diff --git a/src/syslinux/libfat/Makefile.in b/src/syslinux/libfat/Makefile.in index 0d843711..2dc1d313 100644 --- a/src/syslinux/libfat/Makefile.in +++ b/src/syslinux/libfat/Makefile.in @@ -95,7 +95,7 @@ libfat_a_AR = $(AR) $(ARFLAGS) libfat_a_LIBADD = am_libfat_a_OBJECTS = libfat_a-cache.$(OBJEXT) \ libfat_a-fatchain.$(OBJEXT) libfat_a-open.$(OBJEXT) \ - libfat_a-searchdir.$(OBJEXT) + libfat_a-searchdir.$(OBJEXT) libfat_a-dumpdir.$(OBJEXT) libfat_a_OBJECTS = $(am_libfat_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -243,7 +243,7 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ noinst_LIBRARIES = libfat.a -libfat_a_SOURCES = cache.c fatchain.c open.c searchdir.c +libfat_a_SOURCES = cache.c fatchain.c open.c searchdir.c dumpdir.c libfat_a_CFLAGS = $(AM_CFLAGS) all: all-am @@ -324,6 +324,12 @@ libfat_a-searchdir.o: searchdir.c libfat_a-searchdir.obj: searchdir.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfat_a_CFLAGS) $(CFLAGS) -c -o libfat_a-searchdir.obj `if test -f 'searchdir.c'; then $(CYGPATH_W) 'searchdir.c'; else $(CYGPATH_W) '$(srcdir)/searchdir.c'; fi` +libfat_a-dumpdir.o: dumpdir.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfat_a_CFLAGS) $(CFLAGS) -c -o libfat_a-dumpdir.o `test -f 'dumpdir.c' || echo '$(srcdir)/'`dumpdir.c + +libfat_a-dumpdir.obj: dumpdir.c + $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libfat_a_CFLAGS) $(CFLAGS) -c -o libfat_a-dumpdir.obj `if test -f 'dumpdir.c'; then $(CYGPATH_W) 'dumpdir.c'; else $(CYGPATH_W) '$(srcdir)/dumpdir.c'; fi` + ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am diff --git a/src/syslinux/libfat/cache.c b/src/syslinux/libfat/cache.c index 5b989765..51f1f007 100644 --- a/src/syslinux/libfat/cache.c +++ b/src/syslinux/libfat/cache.c @@ -28,7 +28,6 @@ * Also, since struct libfat_sector's data[0] is our buffer, this means we must BOTH * align that member in the struct declaration, and use aligned malloc/free. */ -extern void _uprintf(const char *format, ...); void *libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n) { struct libfat_sector *ls; diff --git a/src/syslinux/libfat/dumpdir.c b/src/syslinux/libfat/dumpdir.c new file mode 100644 index 00000000..bc037189 --- /dev/null +++ b/src/syslinux/libfat/dumpdir.c @@ -0,0 +1,126 @@ +/* ----------------------------------------------------------------------- * + * + * Copyright 2019 Pete Batard + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, Inc., 53 Temple Place Ste 330, + * Boston MA 02111-1307, USA; either version 2 of the License, or + * (at your option) any later version; incorporated herein by reference. + * + * ----------------------------------------------------------------------- */ + +/* + * dumpdir.c + * + * Returns all files and directory items from a FAT directory. + */ + +#include +#include "libfatint.h" + +static struct fat_dirent* get_next_dirent(struct libfat_filesystem *fs, + libfat_sector_t *sector, int *offset) +{ + struct fat_dirent *dep; + + *offset += sizeof(struct fat_dirent); + if (*offset >= LIBFAT_SECTOR_SIZE) { + *offset = 0; + *sector = libfat_nextsector(fs, *sector); + if ((*sector == 0) || (*sector == (libfat_sector_t)-1)) + return NULL; + } + dep = libfat_get_sector(fs, *sector); + if (!dep) + return NULL; + dep = (struct fat_dirent*) &((char*)dep)[*offset]; + return dep; +} + +static void fill_utf16(wchar_t *name, unsigned char *entry) +{ + int i; + for (i=0; i<5; i++) + name[i] = read16((le16_t*)&entry[1 + 2*i]); + for (i=5; i<11; i++) + name[i] = read16((le16_t*)&entry[4 + 2*i]); + for (i=11; i<12; i++) + name[i] = read16((le16_t*)&entry[6 + 2*i]); +} + +int libfat_dumpdir(struct libfat_filesystem *fs, libfat_dirpos_t *dp, + libfat_diritem_t *di) +{ + int i, j; + struct fat_dirent *dep; + + memset(di->name, 0, sizeof(di->name)); + di->size = 0; + di->attributes = 0; + if (dp->offset < 0) { + /* First entry */ + dp->offset = 0; + dp->sector = libfat_clustertosector(fs, dp->cluster); + if ((dp->sector == 0) || (dp->sector == (libfat_sector_t)-1)) + return -1; + dep = libfat_get_sector(fs, dp->sector); + } else { + dep = get_next_dirent(fs, &dp->sector, &dp->offset); + } + if (!dep) + return -1; /* Read error */ + + /* Ignore volume labels, deleted entries as well as '.' and '..' entries */ + while ((dep->attribute == 0x08) || (dep->name[0] == 0xe5) || + ((dep->name[0] == '.') && (dep->name[2] == ' ') && + ((dep->name[1] == ' ') || (dep->name[1] == '.')))) { + dep = get_next_dirent(fs, &dp->sector, &dp->offset); + if (!dep) + return -1; + } + + if (dep->name[0] == 0) + return -2; /* Last entry */ + + /* Build UCS-2 name */ + j = -1; + while (dep->attribute == 0x0F) { /* LNF (Long File Name) entry */ + i = dep->name[0]; + if ((j < 0) && ((i & 0xF0) != 0x40)) /* End of LFN marker was not found */ + break; + /* Isolate and check the sequence number, which should be decrementing */ + i = (i & 0x0F) - 1; + if ((j >= 0) && (i != j - 1)) + return -3; + j = i; + fill_utf16(&di->name[13 * i], dep->name); + dep = get_next_dirent(fs, &dp->sector, &dp->offset); + if (!dep) + return -1; + } + + if (di->name[0] == 0) { + for (i = 0, j = 0; i < 12; i++) { + if ((i >= 8) && (dep->name[i] == ' ')) + break; + if (i == 8) + di->name[j++] = '.'; + if (dep->name[i] == ' ') + continue; + di->name[j] = dep->name[i]; + /* Caseflags: bit 3 = lowercase basename, bit 4 = lowercase extension */ + if ((di->name[j] >= 'A') && (di->name[j] <= 'Z')) { + if ((dep->caseflags & 0x02) && (i < 8)) + di->name[j] += 0x20; + if ((dep->caseflags & 0x04) && (i >= 8)) + di->name[j] += 0x20; + } + j++; + } + } + + di->attributes = dep->attribute & 0x37; + di->size = read32(&dep->size); + return read16(&dep->clustlo) + (read16(&dep->clusthi) << 16); +} diff --git a/src/syslinux/libfat/libfat.h b/src/syslinux/libfat/libfat.h index 0357510a..d2968822 100644 --- a/src/syslinux/libfat/libfat.h +++ b/src/syslinux/libfat/libfat.h @@ -39,6 +39,18 @@ struct libfat_direntry { unsigned char entry[32]; }; +typedef struct libfat_dirpos { + int32_t cluster; + int32_t offset; + libfat_sector_t sector; +} libfat_dirpos_t; + +typedef struct libfat_diritem { + wchar_t name[256]; + uint32_t size; + uint8_t attributes; /* [--ad-shr] */ +} libfat_diritem_t; + /* * Open the filesystem. The readfunc is the function to read * sectors, in the format: @@ -86,4 +98,14 @@ void *libfat_get_sector(struct libfat_filesystem *fs, libfat_sector_t n); int32_t libfat_searchdir(struct libfat_filesystem *fs, int32_t dirclust, const void *name, struct libfat_direntry *direntry); +/* + * Return all the files and directory items from a FAT directory. + * Initial call must set dp->offset to negative and dp->cluster to the cluster + * that contains the directory data. After that each subsequent call must use + * the same dp. + * Return value is the cluster for the corresponding item or negative on error. + */ +int libfat_dumpdir(struct libfat_filesystem *fs, libfat_dirpos_t *dp, + libfat_diritem_t *di); + #endif /* LIBFAT_H */