diff --git a/HBC/META.XML b/HBC/META.XML index 3a148aba..5b8486e5 100644 --- a/HBC/META.XML +++ b/HBC/META.XML @@ -2,8 +2,8 @@ USB Loader GX USB Loader GX Team - 1.0 r943 - 201009161155 + 1.0 r944 + 201009161846 Loads games from USB-devices USB Loader GX is a libwiigui based USB iso loader with a wii-like GUI. You can install games to your HDDs and boot them with shorter loading times. The interactive GUI is completely controllable with WiiMote, Classic Controller or GC Controller. diff --git a/data/app_booter.dol b/data/app_booter.dol new file mode 100644 index 00000000..2f1df769 Binary files /dev/null and b/data/app_booter.dol differ diff --git a/gui.pnproj b/gui.pnproj index d687d797..47a1608a 100644 --- a/gui.pnproj +++ b/gui.pnproj @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/source/homebrewboot/BootHomebrew.c b/source/homebrewboot/BootHomebrew.c deleted file mode 100644 index 8e5d1e12..00000000 --- a/source/homebrewboot/BootHomebrew.c +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include "../lstub.h" - -#include "fatmounter.h" -#include "dolloader.h" -#include "elfloader.h" - -void *innetbuffer = NULL; - -int AllocHomebrewMemory(u32 filesize) { - - innetbuffer = malloc(filesize); - - if (!innetbuffer) - return -1; - - return 1; - -} - -void FreeHomebrewBuffer() { - free(innetbuffer); - innetbuffer = NULL; -} - -void CopyHomebrewMemory(u32 read, u8 *temp, u32 len) { - - memcpy(((u8 *) innetbuffer)+read, temp, len); - -} - -int BootHomebrew(char * path) { - loadStub(); - if (Set_Stub_Split(0x00010001,"UNEO")<0) - Set_Stub_Split(0x00010001,"ULNR"); - void *buffer = NULL; - u32 filesize = 0; - entrypoint entry; - u32 cpu_isr; - - FILE * file = fopen(path, "rb"); - if (!file) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - - fseek (file, 0, SEEK_END); - filesize = ftell(file); - rewind(file); - - buffer = malloc(filesize); - - if (fread (buffer, 1, filesize, file) != filesize) { - fclose (file); - free(buffer); - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - fclose (file); - - struct __argv args; - bzero(&args, sizeof(args)); - args.argvMagic = ARGV_MAGIC; - args.length = strlen(path) + 2; - args.commandLine = (char*)malloc(args.length); - if (!args.commandLine) SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - strcpy(args.commandLine, path); - args.commandLine[args.length - 1] = '\0'; - args.argc = 1; - args.argv = &args.commandLine; - args.endARGV = args.argv + 1; - - int ret = valid_elf_image(buffer); - if (ret == 1) - entry = (entrypoint) load_elf_image(buffer); - else - entry = (entrypoint) load_dol(buffer, &args); - - free(buffer); - - if (!entry) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - SDCard_deInit(); - USBDevice_deInit(); - - WPAD_Flush(0); - WPAD_Disconnect(0); - WPAD_Shutdown(); - - SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); - _CPU_ISR_Disable (cpu_isr); - __exception_closeall(); - entry(); - _CPU_ISR_Restore (cpu_isr); - - return 0; -} - -int BootHomebrewFromMem() { - loadStub(); - if (Set_Stub_Split(0x00010001,"UNEO")<0) - Set_Stub_Split(0x00010001,"ULNR"); - entrypoint entry; - u32 cpu_isr; - - if (!innetbuffer) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - struct __argv args; - - int ret = valid_elf_image(innetbuffer); - if (ret == 1) - entry = (entrypoint) load_elf_image(innetbuffer); - else - entry = (entrypoint) load_dol(innetbuffer, &args); - - free(innetbuffer); - - if (!entry) { - SDCard_deInit(); - USBDevice_deInit(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - - SDCard_deInit(); - USBDevice_deInit(); - - WPAD_Flush(0); - WPAD_Disconnect(0); - WPAD_Shutdown(); - - SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); - _CPU_ISR_Disable (cpu_isr); - __exception_closeall(); - entry(); - _CPU_ISR_Restore (cpu_isr); - - return 0; -} diff --git a/source/homebrewboot/BootHomebrew.cpp b/source/homebrewboot/BootHomebrew.cpp new file mode 100644 index 00000000..d33ede39 --- /dev/null +++ b/source/homebrewboot/BootHomebrew.cpp @@ -0,0 +1,131 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "usbloader/usbstorage2.h" +#include "dolloader.h" +#include "fatmounter.h" +#include "sys.h" + +extern const u8 app_booter_dol[]; +extern const u32 app_booter_dol_size; + +static u8 *homebrewbuffer = (u8 *) 0x92000000; +static int homebrewsize = 0; +static std::vector Arguments; + +void AddBootArgument(const char * argv) +{ + std::string arg(argv); + Arguments.push_back(arg); +} + +int CopyHomebrewMemory(u8 *temp, u32 pos, u32 len) +{ + homebrewsize += len; + memcpy((homebrewbuffer)+pos, temp, len); + + return 1; +} + +void FreeHomebrewBuffer() +{ + homebrewbuffer = (u8 *)0x92000000; + homebrewsize = 0; +} + +static int SetupARGV(struct __argv * args) +{ + if(!args) + return -1; + + bzero(args, sizeof(struct __argv)); + args->argvMagic = ARGV_MAGIC; + + u32 stringlength = 1; + + /** Append Arguments **/ + for(u32 i = 0; i < Arguments.size(); i++) + { + stringlength += Arguments[i].size()+1; + } + + args->length = stringlength; + args->commandLine = (char*) malloc(args->length); + if (!args->commandLine) + return -1; + + u32 argc = 0; + u32 position = 0; + + /** Append Arguments **/ + for(u32 i = 0; i < Arguments.size(); i++) + { + strcpy(&args->commandLine[position], Arguments[i].c_str()); + position += Arguments[i].size()+1; + argc++; + } + + args->argc = argc; + + args->commandLine[args->length - 1] = '\0'; + args->argv = &args->commandLine; + args->endARGV = args->argv + 1; + + Arguments.clear(); + + return 0; +} + +int BootHomebrew() +{ + if(homebrewsize <= 0) + Sys_BackToLoader(); + + SDCard_deInit(); + USBDevice_deInit(); + USBStorage2_Deinit(); + + struct __argv args; + SetupARGV(&args); + + u32 cpu_isr; + + entrypoint entry = (entrypoint) load_dol(app_booter_dol, &args); + + if (!entry) + Sys_BackToLoader(); + + SYS_ResetSystem(SYS_SHUTDOWN, 0, 0); + _CPU_ISR_Disable (cpu_isr); + __exception_closeall(); + entry(); + _CPU_ISR_Restore (cpu_isr); + + return 0; +} + +int BootHomebrew(const char * filepath) +{ + FILE * file = fopen(filepath, "rb"); + if(!file) + Sys_BackToLoader(); + + fseek(file, 0, SEEK_END); + + int size = ftell(file); + rewind(file); + + homebrewsize = fread(homebrewbuffer, 1, size, file); + fclose(file); + + AddBootArgument(filepath); + + return BootHomebrew(); +} diff --git a/source/homebrewboot/BootHomebrew.h b/source/homebrewboot/BootHomebrew.h index 60f64427..b58b3c07 100644 --- a/source/homebrewboot/BootHomebrew.h +++ b/source/homebrewboot/BootHomebrew.h @@ -1,13 +1,11 @@ #ifndef _BOOTHOMEBREW_H_ #define _BOOTHOMEBREW_H_ - -int BootHomebrew(char * path); -int BootHomebrewFromMem(); -void CopyHomebrewMemory(u32 read, u8 *temp, u32 len); -int AllocHomebrewMemory(u32 filesize); +int BootHomebrew(); +int BootHomebrew(const char * filepath); +int CopyHomebrewMemory(u8 *temp, u32 pos, u32 len); +void AddBootArgument(const char * arg); void FreeHomebrewBuffer(); -void AddBootArgument(const char * argv); -extern u32 homebrewsize; +int LoadHomebrew(const char * filepath); #endif diff --git a/source/homebrewboot/HomebrewBrowse.cpp b/source/homebrewboot/HomebrewBrowse.cpp index a78b965c..1a21c8e7 100644 --- a/source/homebrewboot/HomebrewBrowse.cpp +++ b/source/homebrewboot/HomebrewBrowse.cpp @@ -41,7 +41,6 @@ extern char wiiloadVersion[2]; extern u8 shutdown; extern u8 reset; extern struct SSettings Settings; -extern void *innetbuffer; /*** Variables used elsewhere ***/ u8 boothomebrew = 0; @@ -794,124 +793,118 @@ int MenuHomebrewBrowse() { int choice = WindowPrompt(filesizetxt, temp, tr("OK"), tr("Cancel")); - if (choice == 1) { + if (choice == 1) + { + u32 read = 0; + u8 *temp = NULL; + int len = NETWORKBLOCKSIZE; + temp = (u8 *) malloc(infilesize); - int res = AllocHomebrewMemory(infilesize); + bool error = false; + u8 *ptr = temp; + while (read < infilesize) { - if (res < 0) { - CloseConnection(); - WindowPrompt(tr("Not enough free memory."), 0, tr("OK")); - } else { - u32 read = 0; - u8 *temp = NULL; - int len = NETWORKBLOCKSIZE; - temp = (u8 *) malloc(infilesize); + ShowProgress(tr("Receiving file from:"), GetIncommingIP(), NULL, read, infilesize, true); - bool error = false; - u8 *ptr = temp; - while (read < infilesize) { + if (infilesize - read < (u32) len) + len = infilesize-read; + else + len = NETWORKBLOCKSIZE; - ShowProgress(tr("Receiving file from:"), GetIncommingIP(), NULL, read, infilesize, true); + int result = network_read(ptr, len); - if (infilesize - read < (u32) len) - len = infilesize-read; - else - len = NETWORKBLOCKSIZE; - - int result = network_read(ptr, len); - - if (result < 0) { - WindowPrompt(tr("Error while transfering data."), 0, tr("OK")); - error = true; - break; - } - if (!result) { - break; - } - - ptr += result; - - read += result; + if (result < 0) { + WindowPrompt(tr("Error while transfering data."), 0, tr("OK")); + error = true; + break; + } + if (!result) { + break; } - - char filename[101]; - if (!error) { - - network_read((u8*) &filename, 100); - - // Do we need to unzip this thing? - if (wiiloadVersion[0] > 0 || wiiloadVersion[1] > 4) { - // We need to unzip... - if (temp[0] == 'P' && temp[1] == 'K' && temp[2] == 0x03 && temp[3] == 0x04) { - // It's a zip file, unzip to the apps directory - - // Zip archive, ask for permission to install the zip - char zippath[255]; - sprintf((char *) &zippath, "%s%s", Settings.homebrewapps_path, filename); - - FILE *fp = fopen(zippath, "wb"); - if (fp != NULL) - { - fwrite(temp, 1, infilesize, fp); - fclose(fp); - - // Now unzip the zip file... - unzFile uf = unzOpen(zippath); - if (uf==NULL) { - error = true; - } else { - extractZip(uf,0,1,0, Settings.homebrewapps_path); - unzCloseCurrentFile(uf); - - remove(zippath); - - // Reload this menu here... - menu = MENU_HOMEBREWBROWSE; - break; - } - } else { - error = true; - } - } else if (uncfilesize != 0) { // if uncfilesize == 0, it's not compressed - // It's compressed, uncompress - u8 *unc = (u8 *) malloc(uncfilesize); - uLongf f = uncfilesize; - error = uncompress(unc, &f, temp, infilesize) != Z_OK; - uncfilesize = f; - homebrewsize = uncfilesize; - - free(temp); - temp = unc; - } - } - - if (!error && strstr(filename,".zip") == NULL) { - innetbuffer = temp; + ptr += result; - } - } - - ProgressStop(); + read += result; + } - if (error || read != infilesize) { - WindowPrompt(tr("Error:"), tr("No data could be read."), tr("OK")); - FreeHomebrewBuffer(); + char filename[101]; + if (!error) { + + network_read((u8*) &filename, 100); + + // Do we need to unzip this thing? + if (wiiloadVersion[0] > 0 || wiiloadVersion[1] > 4) { + + // We need to unzip... + if (temp[0] == 'P' && temp[1] == 'K' && temp[2] == 0x03 && temp[3] == 0x04) { + // It's a zip file, unzip to the apps directory + + // Zip archive, ask for permission to install the zip + char zippath[255]; + sprintf((char *) &zippath, "%s%s", Settings.homebrewapps_path, filename); + + FILE *fp = fopen(zippath, "wb"); + if (fp != NULL) + { + fwrite(temp, 1, infilesize, fp); + fclose(fp); + + // Now unzip the zip file... + unzFile uf = unzOpen(zippath); + if (uf==NULL) { + error = true; + } else { + extractZip(uf,0,1,0, Settings.homebrewapps_path); + unzCloseCurrentFile(uf); + + remove(zippath); + + // Reload this menu here... + menu = MENU_HOMEBREWBROWSE; + break; + } + } else { + error = true; + } + } else if (uncfilesize != 0) { // if uncfilesize == 0, it's not compressed + // It's compressed, uncompress + u8 *unc = (u8 *) malloc(uncfilesize); + uLongf f = uncfilesize; + error = uncompress(unc, &f, temp, infilesize) != Z_OK; + uncfilesize = f; + infilesize = f; + + free(temp); + temp = unc; + } + } + + if (!error && strstr(filename,".zip") == NULL) { + CopyHomebrewMemory(temp, 0, infilesize); + free(temp); + } + } + + ProgressStop(); + + if (error || read != infilesize) { + WindowPrompt(tr("Error:"), tr("No data could be read."), tr("OK")); + FreeHomebrewBuffer(); + } else { + if (strstr(filename,".dol") || strstr(filename,".DOL") + || strstr(filename,".elf") || strstr(filename,".ELF")) + { + boothomebrew = 2; + AddBootArgument( filename ); + menu = MENU_EXIT; + CloseConnection(); + break; + } else if (strstr(filename,".zip")) { + WindowPrompt(tr("Success:"), tr("Uploaded ZIP file installed to homebrew directory."), tr("OK")); + CloseConnection(); } else { - if (strstr(filename,".dol") || strstr(filename,".DOL") - || strstr(filename,".elf") || strstr(filename,".ELF")) { - boothomebrew = 2; - AddBootArgument( filename ); - menu = MENU_EXIT; - CloseConnection(); - break; - } else if (strstr(filename,".zip")) { - WindowPrompt(tr("Success:"), tr("Uploaded ZIP file installed to homebrew directory."), tr("OK")); - CloseConnection(); - } else { - FreeHomebrewBuffer(); - WindowPrompt(tr("ERROR:"), tr("Not a DOL/ELF file."), tr("OK")); - } + FreeHomebrewBuffer(); + WindowPrompt(tr("ERROR:"), tr("Not a DOL/ELF file."), tr("OK")); } } } diff --git a/source/homebrewboot/dolloader.c b/source/homebrewboot/dolloader.c index 5462e1b4..5edce25d 100644 --- a/source/homebrewboot/dolloader.c +++ b/source/homebrewboot/dolloader.c @@ -20,7 +20,7 @@ typedef struct _dolheader { u32 entry_point; } dolheader; -u32 load_dol(void *dolstart, struct __argv *argv) { +u32 load_dol(const void *dolstart, struct __argv *argv) { u32 i; dolheader *dolfile; @@ -28,15 +28,13 @@ u32 load_dol(void *dolstart, struct __argv *argv) { dolfile = (dolheader *) dolstart; for (i = 0; i < 7; i++) { if ((!dolfile->text_size[i]) || (dolfile->text_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); ICInvalidateRange ((void *) dolfile->text_start[i],dolfile->text_size[i]); - memmove ((void *) dolfile->text_start[i],dolstart+dolfile->text_pos[i],dolfile->text_size[i]); + memcpy((void *) dolfile->text_start[i],dolstart+dolfile->text_pos[i],dolfile->text_size[i]); } for (i = 0; i < 11; i++) { if ((!dolfile->data_size[i]) || (dolfile->data_start[i] < 0x100)) continue; - VIDEO_WaitVSync(); - memmove ((void *) dolfile->data_start[i],dolstart+dolfile->data_pos[i],dolfile->data_size[i]); + memcpy((void *) dolfile->data_start[i],dolstart+dolfile->data_pos[i],dolfile->data_size[i]); DCFlushRangeNoSync ((void *) dolfile->data_start[i],dolfile->data_size[i]); } @@ -45,7 +43,7 @@ u32 load_dol(void *dolstart, struct __argv *argv) { if (argv && argv->argvMagic == ARGV_MAGIC) { void *new_argv = (void *)(dolfile->entry_point + 8); - memmove(new_argv, argv, sizeof(*argv)); + memcpy(new_argv, argv, sizeof(*argv)); DCFlushRange(new_argv, sizeof(*argv)); } diff --git a/source/homebrewboot/dolloader.h b/source/homebrewboot/dolloader.h index 288d9f5a..af644414 100644 --- a/source/homebrewboot/dolloader.h +++ b/source/homebrewboot/dolloader.h @@ -5,10 +5,10 @@ extern "C" { #endif - extern void __exception_closeall(); - typedef void (*entrypoint) (void); +extern void __exception_closeall(); +typedef void (*entrypoint) (void); - u32 load_dol(void *dolstart, struct __argv *argv); +u32 load_dol(const void *dolstart, struct __argv *argv); #ifdef __cplusplus diff --git a/source/homebrewboot/elf_abi.h b/source/homebrewboot/elf_abi.h deleted file mode 100644 index 994912ca..00000000 --- a/source/homebrewboot/elf_abi.h +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Copyright (c) 1995, 1996, 2001, 2002 - * Erik Theisen. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * This is the ELF ABI header file - * formerly known as "elf_abi.h". - */ - -#ifndef _ELF_ABI_H -#define _ELF_ABI_H - -#include - -/* - * This version doesn't work for 64-bit ABIs - Erik. - */ - -/* - * These typedefs need to be handled better. - */ -typedef u32 Elf32_Addr; /* Unsigned program address */ -typedef u32 Elf32_Off; /* Unsigned file offset */ -typedef s32 Elf32_Sword; /* Signed large integer */ -typedef u32 Elf32_Word; /* Unsigned large integer */ -typedef u16 Elf32_Half; /* Unsigned medium integer */ - -/* e_ident[] identification indexes */ -#define EI_MAG0 0 /* file ID */ -#define EI_MAG1 1 /* file ID */ -#define EI_MAG2 2 /* file ID */ -#define EI_MAG3 3 /* file ID */ -#define EI_CLASS 4 /* file class */ -#define EI_DATA 5 /* data encoding */ -#define EI_VERSION 6 /* ELF header version */ -#define EI_OSABI 7 /* OS/ABI specific ELF extensions */ -#define EI_ABIVERSION 8 /* ABI target version */ -#define EI_PAD 9 /* start of pad bytes */ -#define EI_NIDENT 16 /* Size of e_ident[] */ - -/* e_ident[] magic number */ -#define ELFMAG0 0x7f /* e_ident[EI_MAG0] */ -#define ELFMAG1 'E' /* e_ident[EI_MAG1] */ -#define ELFMAG2 'L' /* e_ident[EI_MAG2] */ -#define ELFMAG3 'F' /* e_ident[EI_MAG3] */ -#define ELFMAG "\177ELF" /* magic */ -#define SELFMAG 4 /* size of magic */ - -/* e_ident[] file class */ -#define ELFCLASSNONE 0 /* invalid */ -#define ELFCLASS32 1 /* 32-bit objs */ -#define ELFCLASS64 2 /* 64-bit objs */ -#define ELFCLASSNUM 3 /* number of classes */ - -/* e_ident[] data encoding */ -#define ELFDATANONE 0 /* invalid */ -#define ELFDATA2LSB 1 /* Little-Endian */ -#define ELFDATA2MSB 2 /* Big-Endian */ -#define ELFDATANUM 3 /* number of data encode defines */ - -/* e_ident[] OS/ABI specific ELF extensions */ -#define ELFOSABI_NONE 0 /* No extension specified */ -#define ELFOSABI_HPUX 1 /* Hewlett-Packard HP-UX */ -#define ELFOSABI_NETBSD 2 /* NetBSD */ -#define ELFOSABI_LINUX 3 /* Linux */ -#define ELFOSABI_SOLARIS 6 /* Sun Solaris */ -#define ELFOSABI_AIX 7 /* AIX */ -#define ELFOSABI_IRIX 8 /* IRIX */ -#define ELFOSABI_FREEBSD 9 /* FreeBSD */ -#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX */ -#define ELFOSABI_MODESTO 11 /* Novell Modesto */ -#define ELFOSABI_OPENBSD 12 /* OpenBSD */ -/* 64-255 Architecture-specific value range */ - -/* e_ident[] ABI Version */ -#define ELFABIVERSION 0 - -/* e_ident */ -#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ - (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ - (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ - (ehdr).e_ident[EI_MAG3] == ELFMAG3) - -/* ELF Header */ -typedef struct elfhdr { - unsigned char e_ident[EI_NIDENT]; /* ELF Identification */ - Elf32_Half e_type; /* object file type */ - Elf32_Half e_machine; /* machine */ - Elf32_Word e_version; /* object file version */ - Elf32_Addr e_entry; /* virtual entry point */ - Elf32_Off e_phoff; /* program header table offset */ - Elf32_Off e_shoff; /* section header table offset */ - Elf32_Word e_flags; /* processor-specific flags */ - Elf32_Half e_ehsize; /* ELF header size */ - Elf32_Half e_phentsize; /* program header entry size */ - Elf32_Half e_phnum; /* number of program header entries */ - Elf32_Half e_shentsize; /* section header entry size */ - Elf32_Half e_shnum; /* number of section header entries */ - Elf32_Half e_shstrndx; /* section header table's "section - header string table" entry offset */ -} Elf32_Ehdr; - -/* e_type */ -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* relocatable file */ -#define ET_EXEC 2 /* executable file */ -#define ET_DYN 3 /* shared object file */ -#define ET_CORE 4 /* core file */ -#define ET_NUM 5 /* number of types */ -#define ET_LOOS 0xfe00 /* reserved range for operating */ -#define ET_HIOS 0xfeff /* system specific e_type */ -#define ET_LOPROC 0xff00 /* reserved range for processor */ -#define ET_HIPROC 0xffff /* specific e_type */ - -/* e_machine */ -#define EM_NONE 0 /* No Machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SPARC */ -#define EM_386 3 /* Intel 80386 */ -#define EM_68K 4 /* Motorola 68000 */ -#define EM_88K 5 /* Motorola 88000 */ -#if 0 -#define EM_486 6 /* RESERVED - was Intel 80486 */ -#endif -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 Big-Endian only */ -#define EM_S370 9 /* IBM System/370 Processor */ -#define EM_MIPS_RS4_BE 10 /* MIPS R4000 Big-Endian */ -#if 0 -#define EM_SPARC64 11 /* RESERVED - was SPARC v9 -64-bit unoffical */ -#endif -/* RESERVED 11-14 for future use */ -#define EM_PARISC 15 /* HPPA */ -/* RESERVED 16 for future use */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Enhanced instruction set SPARC */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* 64-bit PowerPC */ -#define EM_S390 22 /* IBM System/390 Processor */ -/* RESERVED 23-35 for future use */ -#define EM_V800 36 /* NEC V800 */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* Advanced Risc Machines ARM */ -#define EM_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC Version 9 */ -#define EM_TRICORE 44 /* Siemens TriCore embedded processor */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -#define EM_X86_64 62 /* AMD x86-64 */ -#define EM_PDSP 63 /* Sony DSP Processor */ -/* RESERVED 64,65 for future use */ -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CHRIS 76 /* Axis Communications embedded proc. */ -#define EM_JAVELIN 77 /* Infineon Technologies emb. proc. */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's edu 64-bit proc. */ -#define EM_HUANY 81 /* Harvard University mach-indep objs */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi DV10V */ -#define EM_D30V 86 /* Mitsubishi DV30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10200 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_NUM 92 /* number of machine types */ - -/* Version */ -#define EV_NONE 0 /* Invalid */ -#define EV_CURRENT 1 /* Current */ -#define EV_NUM 2 /* number of versions */ - -/* Section Header */ -typedef struct { - Elf32_Word sh_name; /* name - index into section header - string table section */ - Elf32_Word sh_type; /* type */ - Elf32_Word sh_flags; /* flags */ - Elf32_Addr sh_addr; /* address */ - Elf32_Off sh_offset; /* file offset */ - Elf32_Word sh_size; /* section size */ - Elf32_Word sh_link; /* section header table index link */ - Elf32_Word sh_info; /* extra information */ - Elf32_Word sh_addralign; /* address alignment */ - Elf32_Word sh_entsize; /* section entry size */ -} Elf32_Shdr; - -/* Special Section Indexes */ -#define SHN_UNDEF 0 /* undefined */ -#define SHN_LORESERVE 0xff00 /* lower bounds of reserved indexes */ -#define SHN_LOPROC 0xff00 /* reserved range for processor */ -#define SHN_HIPROC 0xff1f /* specific section indexes */ -#define SHN_LOOS 0xff20 /* reserved range for operating */ -#define SHN_HIOS 0xff3f /* specific semantics */ -#define SHN_ABS 0xfff1 /* absolute value */ -#define SHN_COMMON 0xfff2 /* common symbol */ -#define SHN_XINDEX 0xffff /* Index is an extra table */ -#define SHN_HIRESERVE 0xffff /* upper bounds of reserved indexes */ - -/* sh_type */ -#define SHT_NULL 0 /* inactive */ -#define SHT_PROGBITS 1 /* program defined information */ -#define SHT_SYMTAB 2 /* symbol table section */ -#define SHT_STRTAB 3 /* string table section */ -#define SHT_RELA 4 /* relocation section with addends*/ -#define SHT_HASH 5 /* symbol hash table section */ -#define SHT_DYNAMIC 6 /* dynamic section */ -#define SHT_NOTE 7 /* note section */ -#define SHT_NOBITS 8 /* no space section */ -#define SHT_REL 9 /* relation section without addends */ -#define SHT_SHLIB 10 /* reserved - purpose unknown */ -#define SHT_DYNSYM 11 /* dynamic symbol table section */ -#define SHT_INIT_ARRAY 14 /* Array of constructors */ -#define SHT_FINI_ARRAY 15 /* Array of destructors */ -#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ -#define SHT_GROUP 17 /* Section group */ -#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ -#define SHT_NUM 19 /* number of section types */ -#define SHT_LOOS 0x60000000 /* Start OS-specific */ -#define SHT_HIOS 0x6fffffff /* End OS-specific */ -#define SHT_LOPROC 0x70000000 /* reserved range for processor */ -#define SHT_HIPROC 0x7fffffff /* specific section header types */ -#define SHT_LOUSER 0x80000000 /* reserved range for application */ -#define SHT_HIUSER 0xffffffff /* specific indexes */ - -/* Section names */ -#define ELF_BSS ".bss" /* uninitialized data */ -#define ELF_COMMENT ".comment" /* version control information */ -#define ELF_DATA ".data" /* initialized data */ -#define ELF_DATA1 ".data1" /* initialized data */ -#define ELF_DEBUG ".debug" /* debug */ -#define ELF_DYNAMIC ".dynamic" /* dynamic linking information */ -#define ELF_DYNSTR ".dynstr" /* dynamic string table */ -#define ELF_DYNSYM ".dynsym" /* dynamic symbol table */ -#define ELF_FINI ".fini" /* termination code */ -#define ELF_FINI_ARRAY ".fini_array" /* Array of destructors */ -#define ELF_GOT ".got" /* global offset table */ -#define ELF_HASH ".hash" /* symbol hash table */ -#define ELF_INIT ".init" /* initialization code */ -#define ELF_INIT_ARRAY ".init_array" /* Array of constuctors */ -#define ELF_INTERP ".interp" /* Pathname of program interpreter */ -#define ELF_LINE ".line" /* Symbolic line numnber information */ -#define ELF_NOTE ".note" /* Contains note section */ -#define ELF_PLT ".plt" /* Procedure linkage table */ -#define ELF_PREINIT_ARRAY ".preinit_array" /* Array of pre-constructors */ -#define ELF_REL_DATA ".rel.data" /* relocation data */ -#define ELF_REL_FINI ".rel.fini" /* relocation termination code */ -#define ELF_REL_INIT ".rel.init" /* relocation initialization code */ -#define ELF_REL_DYN ".rel.dyn" /* relocaltion dynamic link info */ -#define ELF_REL_RODATA ".rel.rodata" /* relocation read-only data */ -#define ELF_REL_TEXT ".rel.text" /* relocation code */ -#define ELF_RODATA ".rodata" /* read-only data */ -#define ELF_RODATA1 ".rodata1" /* read-only data */ -#define ELF_SHSTRTAB ".shstrtab" /* section header string table */ -#define ELF_STRTAB ".strtab" /* string table */ -#define ELF_SYMTAB ".symtab" /* symbol table */ -#define ELF_SYMTAB_SHNDX ".symtab_shndx"/* symbol table section index */ -#define ELF_TBSS ".tbss" /* thread local uninit data */ -#define ELF_TDATA ".tdata" /* thread local init data */ -#define ELF_TDATA1 ".tdata1" /* thread local init data */ -#define ELF_TEXT ".text" /* code */ - -/* Section Attribute Flags - sh_flags */ -#define SHF_WRITE 0x1 /* Writable */ -#define SHF_ALLOC 0x2 /* occupies memory */ -#define SHF_EXECINSTR 0x4 /* executable */ -#define SHF_MERGE 0x10 /* Might be merged */ -#define SHF_STRINGS 0x20 /* Contains NULL terminated strings */ -#define SHF_INFO_LINK 0x40 /* sh_info contains SHT index */ -#define SHF_LINK_ORDER 0x80 /* Preserve order after combining*/ -#define SHF_OS_NONCONFORMING 0x100 /* Non-standard OS specific handling */ -#define SHF_GROUP 0x200 /* Member of section group */ -#define SHF_TLS 0x400 /* Thread local storage */ -#define SHF_MASKOS 0x0ff00000 /* OS specific */ -#define SHF_MASKPROC 0xf0000000 /* reserved bits for processor */ -/* specific section attributes */ - -/* Section Group Flags */ -#define GRP_COMDAT 0x1 /* COMDAT group */ -#define GRP_MASKOS 0x0ff00000 /* Mask OS specific flags */ -#define GRP_MASKPROC 0xf0000000 /* Mask processor specific flags */ - -/* Symbol Table Entry */ -typedef struct elf32_sym { - Elf32_Word st_name; /* name - index into string table */ - Elf32_Addr st_value; /* symbol value */ - Elf32_Word st_size; /* symbol size */ - unsigned char st_info; /* type and binding */ - unsigned char st_other; /* 0 - no defined meaning */ - Elf32_Half st_shndx; /* section header index */ -} Elf32_Sym; - -/* Symbol table index */ -#define STN_UNDEF 0 /* undefined */ - -/* Extract symbol info - st_info */ -#define ELF32_ST_BIND(x) ((x) >> 4) -#define ELF32_ST_TYPE(x) (((unsigned int) x) & 0xf) -#define ELF32_ST_INFO(b,t) (((b) << 4) + ((t) & 0xf)) -#define ELF32_ST_VISIBILITY(x) ((x) & 0x3) - -/* Symbol Binding - ELF32_ST_BIND - st_info */ -#define STB_LOCAL 0 /* Local symbol */ -#define STB_GLOBAL 1 /* Global symbol */ -#define STB_WEAK 2 /* like global - lower precedence */ -#define STB_NUM 3 /* number of symbol bindings */ -#define STB_LOOS 10 /* reserved range for operating */ -#define STB_HIOS 12 /* system specific symbol bindings */ -#define STB_LOPROC 13 /* reserved range for processor */ -#define STB_HIPROC 15 /* specific symbol bindings */ - -/* Symbol type - ELF32_ST_TYPE - st_info */ -#define STT_NOTYPE 0 /* not specified */ -#define STT_OBJECT 1 /* data object */ -#define STT_FUNC 2 /* function */ -#define STT_SECTION 3 /* section */ -#define STT_FILE 4 /* file */ -#define STT_NUM 5 /* number of symbol types */ -#define STT_TLS 6 /* Thread local storage symbol */ -#define STT_LOOS 10 /* reserved range for operating */ -#define STT_HIOS 12 /* system specific symbol types */ -#define STT_LOPROC 13 /* reserved range for processor */ -#define STT_HIPROC 15 /* specific symbol types */ - -/* Symbol visibility - ELF32_ST_VISIBILITY - st_other */ -#define STV_DEFAULT 0 /* Normal visibility rules */ -#define STV_INTERNAL 1 /* Processor specific hidden class */ -#define STV_HIDDEN 2 /* Symbol unavailable in other mods */ -#define STV_PROTECTED 3 /* Not preemptible, not exported */ - - -/* Relocation entry with implicit addend */ -typedef struct { - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ -} Elf32_Rel; - -/* Relocation entry with explicit addend */ -typedef struct { - Elf32_Addr r_offset; /* offset of relocation */ - Elf32_Word r_info; /* symbol table index and type */ - Elf32_Sword r_addend; -} Elf32_Rela; - -/* Extract relocation info - r_info */ -#define ELF32_R_SYM(i) ((i) >> 8) -#define ELF32_R_TYPE(i) ((unsigned char) (i)) -#define ELF32_R_INFO(s,t) (((s) << 8) + (unsigned char)(t)) - -/* Program Header */ -typedef struct { - Elf32_Word p_type; /* segment type */ - Elf32_Off p_offset; /* segment offset */ - Elf32_Addr p_vaddr; /* virtual address of segment */ - Elf32_Addr p_paddr; /* physical address - ignored? */ - Elf32_Word p_filesz; /* number of bytes in file for seg. */ - Elf32_Word p_memsz; /* number of bytes in mem. for seg. */ - Elf32_Word p_flags; /* flags */ - Elf32_Word p_align; /* memory alignment */ -} Elf32_Phdr; - -/* Segment types - p_type */ -#define PT_NULL 0 /* unused */ -#define PT_LOAD 1 /* loadable segment */ -#define PT_DYNAMIC 2 /* dynamic linking section */ -#define PT_INTERP 3 /* the RTLD */ -#define PT_NOTE 4 /* auxiliary information */ -#define PT_SHLIB 5 /* reserved - purpose undefined */ -#define PT_PHDR 6 /* program header */ -#define PT_TLS 7 /* Thread local storage template */ -#define PT_NUM 8 /* Number of segment types */ -#define PT_LOOS 0x60000000 /* reserved range for operating */ -#define PT_HIOS 0x6fffffff /* system specific segment types */ -#define PT_LOPROC 0x70000000 /* reserved range for processor */ -#define PT_HIPROC 0x7fffffff /* specific segment types */ - -/* Segment flags - p_flags */ -#define PF_X 0x1 /* Executable */ -#define PF_W 0x2 /* Writable */ -#define PF_R 0x4 /* Readable */ -#define PF_MASKOS 0x0ff00000 /* OS specific segment flags */ -#define PF_MASKPROC 0xf0000000 /* reserved bits for processor */ -/* specific segment flags */ -/* Dynamic structure */ -typedef struct { - Elf32_Sword d_tag; /* controls meaning of d_val */ - union { - Elf32_Word d_val; /* Multiple meanings - see d_tag */ - Elf32_Addr d_ptr; /* program virtual address */ - } d_un; -} Elf32_Dyn; - -extern Elf32_Dyn _DYNAMIC[]; - -/* Dynamic Array Tags - d_tag */ -#define DT_NULL 0 /* marks end of _DYNAMIC array */ -#define DT_NEEDED 1 /* string table offset of needed lib */ -#define DT_PLTRELSZ 2 /* size of relocation entries in PLT */ -#define DT_PLTGOT 3 /* address PLT/GOT */ -#define DT_HASH 4 /* address of symbol hash table */ -#define DT_STRTAB 5 /* address of string table */ -#define DT_SYMTAB 6 /* address of symbol table */ -#define DT_RELA 7 /* address of relocation table */ -#define DT_RELASZ 8 /* size of relocation table */ -#define DT_RELAENT 9 /* size of relocation entry */ -#define DT_STRSZ 10 /* size of string table */ -#define DT_SYMENT 11 /* size of symbol table entry */ -#define DT_INIT 12 /* address of initialization func. */ -#define DT_FINI 13 /* address of termination function */ -#define DT_SONAME 14 /* string table offset of shared obj */ -#define DT_RPATH 15 /* string table offset of library -search path */ -#define DT_SYMBOLIC 16 /* start sym search in shared obj. */ -#define DT_REL 17 /* address of rel. tbl. w addends */ -#define DT_RELSZ 18 /* size of DT_REL relocation table */ -#define DT_RELENT 19 /* size of DT_REL relocation entry */ -#define DT_PLTREL 20 /* PLT referenced relocation entry */ -#define DT_DEBUG 21 /* bugger */ -#define DT_TEXTREL 22 /* Allow rel. mod. to unwritable seg */ -#define DT_JMPREL 23 /* add. of PLT's relocation entries */ -#define DT_BIND_NOW 24 /* Process relocations of object */ -#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ -#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ -#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ -#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ -#define DT_RUNPATH 29 /* Library search path */ -#define DT_FLAGS 30 /* Flags for the object being loaded */ -#define DT_ENCODING 32 /* Start of encoded range */ -#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ -#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ -#define DT_NUM 34 /* Number used. */ -#define DT_LOOS 0x60000000 /* reserved range for OS */ -#define DT_HIOS 0x6fffffff /* specific dynamic array tags */ -#define DT_LOPROC 0x70000000 /* reserved range for processor */ -#define DT_HIPROC 0x7fffffff /* specific dynamic array tags */ - -/* Dynamic Tag Flags - d_un.d_val */ -#define DF_ORIGIN 0x01 /* Object may use DF_ORIGIN */ -#define DF_SYMBOLIC 0x02 /* Symbol resolutions starts here */ -#define DF_TEXTREL 0x04 /* Object contains text relocations */ -#define DF_BIND_NOW 0x08 /* No lazy binding for this object */ -#define DF_STATIC_TLS 0x10 /* Static thread local storage */ - -/* Standard ELF hashing function */ -unsigned long elf_hash(const unsigned char *name); - -#define ELF_TARG_VER 1 /* The ver for which this code is intended */ - -/* - * XXX - PowerPC defines really don't belong in here, - * but we'll put them in for simplicity. - */ - -/* Values for Elf32/64_Ehdr.e_flags. */ -#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ - -/* Cygnus local bits below */ -#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ -#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib -flag */ - -/* PowerPC relocations defined by the ABIs */ -#define R_PPC_NONE 0 -#define R_PPC_ADDR32 1 /* 32bit absolute address */ -#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ -#define R_PPC_ADDR16 3 /* 16bit absolute address */ -#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ -#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ -#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ -#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ -#define R_PPC_ADDR14_BRTAKEN 8 -#define R_PPC_ADDR14_BRNTAKEN 9 -#define R_PPC_REL24 10 /* PC relative 26 bit */ -#define R_PPC_REL14 11 /* PC relative 16 bit */ -#define R_PPC_REL14_BRTAKEN 12 -#define R_PPC_REL14_BRNTAKEN 13 -#define R_PPC_GOT16 14 -#define R_PPC_GOT16_LO 15 -#define R_PPC_GOT16_HI 16 -#define R_PPC_GOT16_HA 17 -#define R_PPC_PLTREL24 18 -#define R_PPC_COPY 19 -#define R_PPC_GLOB_DAT 20 -#define R_PPC_JMP_SLOT 21 -#define R_PPC_RELATIVE 22 -#define R_PPC_LOCAL24PC 23 -#define R_PPC_UADDR32 24 -#define R_PPC_UADDR16 25 -#define R_PPC_REL32 26 -#define R_PPC_PLT32 27 -#define R_PPC_PLTREL32 28 -#define R_PPC_PLT16_LO 29 -#define R_PPC_PLT16_HI 30 -#define R_PPC_PLT16_HA 31 -#define R_PPC_SDAREL16 32 -#define R_PPC_SECTOFF 33 -#define R_PPC_SECTOFF_LO 34 -#define R_PPC_SECTOFF_HI 35 -#define R_PPC_SECTOFF_HA 36 -/* Keep this the last entry. */ -#define R_PPC_NUM 37 - -/* The remaining relocs are from the Embedded ELF ABI, and are not - in the SVR4 ELF ABI. */ -#define R_PPC_EMB_NADDR32 101 -#define R_PPC_EMB_NADDR16 102 -#define R_PPC_EMB_NADDR16_LO 103 -#define R_PPC_EMB_NADDR16_HI 104 -#define R_PPC_EMB_NADDR16_HA 105 -#define R_PPC_EMB_SDAI16 106 -#define R_PPC_EMB_SDA2I16 107 -#define R_PPC_EMB_SDA2REL 108 -#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ -#define R_PPC_EMB_MRKREF 110 -#define R_PPC_EMB_RELSEC16 111 -#define R_PPC_EMB_RELST_LO 112 -#define R_PPC_EMB_RELST_HI 113 -#define R_PPC_EMB_RELST_HA 114 -#define R_PPC_EMB_BIT_FLD 115 -#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ - -/* Diab tool relocations. */ -#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ -#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ -#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ -#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ -#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ -#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ - -/* This is a phony reloc to handle any old fashioned TOC16 references - that may still be in object files. */ -#define R_PPC_TOC16 255 - -#endif /* _ELF_H */ - diff --git a/source/homebrewboot/elfloader.c b/source/homebrewboot/elfloader.c deleted file mode 100644 index af9354ab..00000000 --- a/source/homebrewboot/elfloader.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2001 William L. Pitts - * Modifications (c) 2004 Felix Domke - * All rights reserved. - * - * Redistribution and use in source and binary forms are freely - * permitted provided that the above copyright notice and this - * paragraph and the following disclaimer are duplicated in all - * such forms. - * - * This software is provided "AS IS" and without any express or - * implied warranties, including, without limitation, the implied - * warranties of merchantability and fitness for a particular - * purpose. - */ - -#include -#include - -#include - -#include "elf_abi.h" - -/* ====================================================================== - * Determine if a valid ELF image exists at the given memory location. - * First looks at the ELF header magic field, the makes sure that it is - * executable and makes sure that it is for a PowerPC. - * ====================================================================== */ -s32 valid_elf_image (void *addr) { - Elf32_Ehdr *ehdr; /* Elf header structure pointer */ - - ehdr = (Elf32_Ehdr *) addr; - - if (!IS_ELF (*ehdr)) - return 0; - - if (ehdr->e_type != ET_EXEC) - return -1; - - if (ehdr->e_machine != EM_PPC) - return -1; - - return 1; -} - - -/* ====================================================================== - * A very simple elf loader, assumes the image is valid, returns the - * entry point address. - * ====================================================================== */ -u32 load_elf_image (void *addr) { - Elf32_Ehdr *ehdr; - Elf32_Shdr *shdr; - u8 *strtab = 0; - u8 *image; - int i; - - ehdr = (Elf32_Ehdr *) addr; - /* Find the section header string table for output info */ - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (ehdr->e_shstrndx * sizeof (Elf32_Shdr))); - - if (shdr->sh_type == SHT_STRTAB) - strtab = (u8 *) (addr + shdr->sh_offset); - - /* Load each appropriate section */ - for (i = 0; i < ehdr->e_shnum; ++i) { - shdr = (Elf32_Shdr *) (addr + ehdr->e_shoff + - (i * sizeof (Elf32_Shdr))); - - if (!(shdr->sh_flags & SHF_ALLOC) - || shdr->sh_addr == 0 || shdr->sh_size == 0) { - continue; - } - - shdr->sh_addr &= 0x3FFFFFFF; - shdr->sh_addr |= 0x80000000; - - if (strtab) { - /*printf ("%sing section %s @ 0x%08x (0x%08x bytes)\n", - (shdr->sh_type == SHT_NOBITS) ? - "clear" : "load", - &strtab[shdr->sh_name], - (u32) shdr->sh_addr, - (u32) shdr->sh_size);*/ - } - - if (shdr->sh_type == SHT_NOBITS) { - memset ((void *) shdr->sh_addr, 0, shdr->sh_size); - } else { - image = (u8 *) addr + shdr->sh_offset; - memcpy ((void *) shdr->sh_addr, - (const void *) image, - shdr->sh_size); - } - DCFlushRangeNoSync ((void *) shdr->sh_addr, shdr->sh_size); - } - - return (ehdr->e_entry & 0x3FFFFFFF) | 0x80000000; -} - diff --git a/source/homebrewboot/elfloader.h b/source/homebrewboot/elfloader.h deleted file mode 100644 index 725aed7e..00000000 --- a/source/homebrewboot/elfloader.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ELFLOADER_H_ -#define _ELFLOADER_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - - s32 valid_elf_image (void *addr); - u32 load_elf_image (void *addr); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/libfat/bit_ops.h b/source/libfat/bit_ops.h index 8a5fb3e1..762be0b3 100644 --- a/source/libfat/bit_ops.h +++ b/source/libfat/bit_ops.h @@ -3,7 +3,7 @@ Functions for dealing with conversion of data between types Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,8 +26,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BIT_OPS_H -#define __BIT_OPS_H +#ifndef _BIT_OPS_H +#define _BIT_OPS_H #include diff --git a/source/libfat/cache.h b/source/libfat/cache.h new file mode 100644 index 00000000..c6e48d07 --- /dev/null +++ b/source/libfat/cache.h @@ -0,0 +1,130 @@ +/* + cache.h + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE_H +#define _CACHE_H + +#include "common.h" +#include "disc.h" + +#define PAGE_SECTORS 64 +#define CACHE_PAGE_SIZE (BYTES_PER_READ * PAGE_SECTORS) + +typedef struct { + sec_t sector; + unsigned int count; + unsigned int last_access; + bool dirty; + uint8_t* cache; +} CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + CACHE_ENTRY* cacheEntries; +} CACHE; + +/* +Read data from a sector in the cache +If the sector is not in the cache, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the cache, zeroing the sector first +If the sector is not in the cache, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the cache +*/ +bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the cache +*/ +static inline bool _FAT_cache_readSector (CACHE* cache, void* buffer, sec_t sector) { + return _FAT_cache_readPartialSector (cache, buffer, sector, 0, BYTES_PER_READ); +} + +/* +Write a full sector to the cache +*/ +static inline bool _FAT_cache_writeSector (CACHE* cache, const void* buffer, sec_t sector) { + return _FAT_cache_writePartialSector (cache, buffer, sector, 0, BYTES_PER_READ); +} + +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the cache +*/ +bool _FAT_cache_flush (CACHE* cache); + +/* +Clear out the contents of the cache without writing any dirty sectors first +*/ +void _FAT_cache_invalidate (CACHE* cache); + +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition); + +void _FAT_cache_destructor (CACHE* cache); + +#endif // _CACHE_H + diff --git a/source/libfat/common.h b/source/libfat/common.h index 85d15db9..98845e05 100644 --- a/source/libfat/common.h +++ b/source/libfat/common.h @@ -3,7 +3,7 @@ Common definitions and included files for the FATlib Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,23 +26,54 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __COMMON_H -#define __COMMON_H +#ifndef _COMMON_H +#define _COMMON_H #define BYTES_PER_READ 512 -#include "fat.h" #include #include +#include "libfat/fat.h" +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif // Platform specific includes -#include -#include -#include +#if defined(__gamecube__) || defined (__wii__) + #include + #include + #include +#elif defined(NDS) + #include + #include + #include +#elif defined(GBA) + #include + #include +#endif + // Platform specific options -#define DEFAULT_CACHE_PAGES 4 -#define DEFAULT_SECTORS_PAGE 64 -#define USE_LWP_LOCK -#define USE_RTC_TIME +#if defined (__wii__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (__gamecube__) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 64 + #define USE_LWP_LOCK + #define USE_RTC_TIME +#elif defined (NDS) + #define DEFAULT_CACHE_PAGES 4 + #define DEFAULT_SECTORS_PAGE 8 + #define USE_RTC_TIME +#elif defined (GBA) + #define DEFAULT_CACHE_PAGES 2 + #define DEFAULT_SECTORS_PAGE 8 + #define LIMIT_SECTORS 128 +#endif #endif // _COMMON_H diff --git a/source/libfat/directory.c b/source/libfat/directory.c index 279a361d..0e7b5ede 100644 --- a/source/libfat/directory.c +++ b/source/libfat/directory.c @@ -329,7 +329,8 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { } lfn[lfnPos] = '\0'; // Set end of lfn to null character lfnChkSum = entryData[LFN_offset_checkSum]; - } if (lfnChkSum != entryData[LFN_offset_checkSum]) { + } + if (lfnChkSum != entryData[LFN_offset_checkSum]) { lfnExists = false; } if (lfnExists) { @@ -417,6 +418,49 @@ bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { return true; } +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { + DIR_ENTRY entry; + DIR_ENTRY_POSITION entryEnd; + uint8_t entryData[DIR_ENTRY_DATA_SIZE]; + int i; + bool end; + + _FAT_directory_getRootEntry(partition, &entry); + + entryEnd = entry.dataEnd; + + // Make sure we are using the correct root directory, in case of FAT32 + if (entryEnd.cluster == FAT16_ROOT_DIR_CLUSTER) { + entryEnd.cluster = partition->rootDirCluster; + } + + label[0]='\0'; + label[11]='\0'; + end = false; + //this entry should be among the first 3 entries in the root directory table, if not, then system can have trouble displaying the right volume label + while(!end) { + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { + end = true; + } + + if(!_FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, entryEnd.cluster) + entryEnd.sector, + entryEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE)) + { //error reading + return false; + } + if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { + for (i = 0; i < 11; i++) { + label[i] = entryData[DIR_ENTRY_name + i]; + } + return true; + } else if (entryData[0] == DIR_ENTRY_LAST) { + end = true; + } + } + return false; +} + bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { DIR_ENTRY_POSITION entryStart = entry->dataStart; DIR_ENTRY_POSITION entryEnd = entry->dataEnd; diff --git a/source/libfat/directory.h b/source/libfat/directory.h index 02bd82ff..93429217 100644 --- a/source/libfat/directory.h +++ b/source/libfat/directory.h @@ -4,7 +4,7 @@ a FAT partition Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,8 +27,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __DIRECTORY_H -#define __DIRECTORY_H +#ifndef _DIRECTORY_H +#define _DIRECTORY_H #include @@ -132,7 +132,7 @@ Returns true on success, false on failure */ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const char* path, const char* pathEnd); -/* +/* Changes the current directory to the one specified by path Returns true on success, false on failure */ @@ -156,10 +156,10 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d /* Get the start cluster of a file from it's entry data */ -uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); +uint32_t _FAT_directory_entryGetCluster (PARTITION* partition, const uint8_t* entryData); -/* -Fill in the file name and entry data of DIR_ENTRY* entry. +/* +Fill in the file name and entry data of DIR_ENTRY* entry. Assumes that the entry's dataStart and dataEnd are correct Returns true on success, false on failure */ @@ -170,4 +170,9 @@ Fill in a stat struct based on a file entry */ void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct stat *st); +/* +Get volume label +*/ +bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label); + #endif // _DIRECTORY_H diff --git a/source/libfat/disc.h b/source/libfat/disc.h new file mode 100644 index 00000000..5c955f90 --- /dev/null +++ b/source/libfat/disc.h @@ -0,0 +1,110 @@ +/* + disc.h + Interface to the low level disc functions. Used by the higher level + file system code. + + Copyright (c) 2006 Michael "Chishm" Chisholm + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef _DISC_H +#define _DISC_H + +#include "common.h" + +/* +A list of all default devices to try at startup, +terminated by a {NULL,NULL} entry. +*/ +typedef struct { + const char* name; + const DISC_INTERFACE* (*getInterface)(void); +} INTERFACE_ID; +extern const INTERFACE_ID _FAT_disc_interfaces[]; + +/* +Check if a disc is inserted +Return true if a disc is inserted and ready, false otherwise +*/ +static inline bool _FAT_disc_isInserted (const DISC_INTERFACE* disc) { + return disc->isInserted(); +} + +/* +Read numSectors sectors from a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to fill +*/ +static inline bool _FAT_disc_readSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, void* buffer) { + return disc->readSectors (sector, numSectors, buffer); +} + +/* +Write numSectors sectors to a disc, starting at sector. +numSectors is between 1 and LIMIT_SECTORS if LIMIT_SECTORS is defined, +else it is at least 1 +sector is 0 or greater +buffer is a pointer to the memory to read from +*/ +static inline bool _FAT_disc_writeSectors (const DISC_INTERFACE* disc, sec_t sector, sec_t numSectors, const void* buffer) { + return disc->writeSectors (sector, numSectors, buffer); +} + +/* +Reset the card back to a ready state +*/ +static inline bool _FAT_disc_clearStatus (const DISC_INTERFACE* disc) { + return disc->clearStatus(); +} + +/* +Initialise the disc to a state ready for data reading or writing +*/ +static inline bool _FAT_disc_startup (const DISC_INTERFACE* disc) { + return disc->startup(); +} + +/* +Put the disc in a state ready for power down. +Complete any pending writes and disable the disc if necessary +*/ +static inline bool _FAT_disc_shutdown (const DISC_INTERFACE* disc) { + return disc->shutdown(); +} + +/* +Return a 32 bit value unique to each type of interface +*/ +static inline uint32_t _FAT_disc_hostType (const DISC_INTERFACE* disc) { + return disc->ioType; +} + +/* +Return a 32 bit value that specifies the capabilities of the disc +*/ +static inline uint32_t _FAT_disc_features (const DISC_INTERFACE* disc) { + return disc->features; +} + +#endif // _DISC_H diff --git a/source/libfat/fat.h b/source/libfat/fat.h index d7b11176..b0ffc13a 100644 --- a/source/libfat/fat.h +++ b/source/libfat/fat.h @@ -1,8 +1,10 @@ /* fat.h Simple functionality for startup, mounting and unmounting of FAT-based devices. - - Copyright (c) 2006 Michael "Chishm" Chisholm + + Copyright (c) 2006 - 2009 + Michael "Chishm" Chisholm + Dave "WinterMute" Murphy Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -34,8 +36,24 @@ extern "C" { #endif +// When compiling for NDS, make sure NDS is defined +#ifndef NDS + #if defined ARM9 || defined ARM7 + #define NDS + #endif +#endif + #include -#include + +#if defined(__gamecube__) || defined (__wii__) +# include +#else +# ifdef NDS +# include "nds/disc_io.h" +# else +# include "disc_io.h" +# endif +#endif /* Initialise any inserted block-devices. @@ -53,7 +71,7 @@ extern bool fatInitDefault (void); /* Mount the device pointed to by interface, and set up a devoptab entry for it as "name:". You can then access the filesystem using "name:/". -This will mount the active partition or the first valid partition on the disc, +This will mount the active partition or the first valid partition on the disc, and will use a cache size optimized for the host system. */ extern bool fatMountSimple (const char* name, const DISC_INTERFACE* interface); @@ -67,12 +85,18 @@ cacheSize specifies the number of pages to allocate for the cache. This will not startup the disc, so you need to call interface->startup(); first. */ extern bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSector, uint32_t cacheSize, uint32_t SectorsPerPage); + /* Unmount the partition specified by name. If there are open files, it will attempt to synchronise them to disc. */ extern void fatUnmount (const char* name); +/* +Get Volume Label +*/ +extern void fatGetVolumeLabel (const char* name, char *label); + #ifdef __cplusplus } #endif diff --git a/source/libfat/fat_cache.c b/source/libfat/fat_cache.c index 422915b8..3fe34537 100644 --- a/source/libfat/fat_cache.c +++ b/source/libfat/fat_cache.c @@ -37,8 +37,8 @@ #include #include "common.h" -#include "fat_cache.h" -#include "disc_fat.h" +#include "cache.h" +#include "disc.h" #include "mem_allocate.h" #include "bit_ops.h" @@ -127,7 +127,7 @@ static CACHE_ENTRY* _FAT_cache_getPage(CACHE *cache,sec_t sector) cacheEntries[i].last_access = accessTime(); return &(cacheEntries[i]); } - + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accesssector > sector) { - + secs_to_write = entry->sector - sector; - + _FAT_disc_writeSectors(cache->disc,sector,secs_to_write,src); src += (secs_to_write*BYTES_PER_READ); sector += secs_to_write; numSectors -= secs_to_write; } - + sec = sector - entry->sector; secs_to_write = entry->count - sec; @@ -327,7 +327,7 @@ bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, cons numSectors -= secs_to_write; entry->dirty = true; - + } else { _FAT_disc_writeSectors(cache->disc,sector,numSectors,src); numSectors=0; diff --git a/source/libfat/disc_fat.c b/source/libfat/fat_disc.c similarity index 73% rename from source/libfat/disc_fat.c rename to source/libfat/fat_disc.c index c17ca4c5..1fac0ed9 100644 --- a/source/libfat/disc_fat.c +++ b/source/libfat/fat_disc.c @@ -2,9 +2,9 @@ disc.c Interface to the low level disc functions. Used by the higher level file system code. - + Copyright (c) 2008 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,7 +27,7 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "disc_fat.h" +#include "disc.h" /* The list of interfaces consists of a series of name/interface pairs. @@ -39,15 +39,16 @@ The list is terminated by a NULL/NULL entry. */ /* ====================== Wii ====================== */ +#if defined (__wii__) #include -#include "usbloader/usbstorage2.h" +#include #include static const DISC_INTERFACE* get_io_wiisd (void) { return &__io_wiisd; } static const DISC_INTERFACE* get_io_usbstorage (void) { - return &__io_usbstorage2; + return &__io_usbstorage; } static const DISC_INTERFACE* get_io_gcsda (void) { @@ -63,5 +64,42 @@ const INTERFACE_ID _FAT_disc_interfaces[] = { {"carda", get_io_gcsda}, {"cardb", get_io_gcsdb}, {NULL, NULL} -}; +}; + +/* ==================== Gamecube ==================== */ +#elif defined (__gamecube__) +#include + +static const DISC_INTERFACE* get_io_gcsda (void) { + return &__io_gcsda; +} +static const DISC_INTERFACE* get_io_gcsdb (void) { + return &__io_gcsdb; +} + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"carda", get_io_gcsda}, + {"cardb", get_io_gcsdb}, + {NULL, NULL} +}; + +/* ====================== NDS ====================== */ +#elif defined (NDS) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", dldiGetInternal}, + {NULL, NULL} +}; + +/* ====================== GBA ====================== */ +#elif defined (GBA) +#include + +const INTERFACE_ID _FAT_disc_interfaces[] = { + {"fat", discGetInterface}, + {NULL, NULL} +}; + +#endif diff --git a/source/libfat/fatdir.c b/source/libfat/fatdir.c index 5a555e38..55faa0b9 100644 --- a/source/libfat/fatdir.c +++ b/source/libfat/fatdir.c @@ -36,7 +36,7 @@ #include "fatdir.h" -#include "fat_cache.h" +#include "cache.h" #include "file_allocation_table.h" #include "partition.h" #include "directory.h" diff --git a/source/libfat/fatdir.h b/source/libfat/fatdir.h index 1b47a914..426dd30b 100644 --- a/source/libfat/fatdir.h +++ b/source/libfat/fatdir.h @@ -1,11 +1,11 @@ /* fatdir.h - - Functions used by the newlib disc stubs to interface with + + Functions used by the newlib disc stubs to interface with this library Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -29,8 +29,8 @@ */ -#ifndef __FATDIR_H -#define __FATDIR_H +#ifndef _FATDIR_H +#define _FATDIR_H #include #include diff --git a/source/libfat/fatfile.c b/source/libfat/fatfile.c index ba8dddb5..0892f960 100644 --- a/source/libfat/fatfile.c +++ b/source/libfat/fatfile.c @@ -39,7 +39,7 @@ #include #include -#include "fat_cache.h" +#include "cache.h" #include "file_allocation_table.h" #include "bit_ops.h" #include "filetime.h" diff --git a/source/libfat/file_allocation_table.h b/source/libfat/file_allocation_table.h index de500496..560c616d 100644 --- a/source/libfat/file_allocation_table.h +++ b/source/libfat/file_allocation_table.h @@ -4,7 +4,7 @@ a FAT partition Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,8 +27,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __FAT_H -#define __FAT_H +#ifndef _FAT_H +#define _FAT_H #include "common.h" #include "partition.h" @@ -36,7 +36,7 @@ #define CLUSTER_EOF_16 0xFFFF #define CLUSTER_EOF 0x0FFFFFFF #define CLUSTER_FREE 0x00000000 -#define CLUSTER_ROOT 0x00000000 +#define CLUSTER_ROOT 0x00000000 #define CLUSTER_FIRST 0x00000002 #define CLUSTER_ERROR 0xFFFFFFFF @@ -58,8 +58,8 @@ uint32_t _FAT_fat_lastCluster (PARTITION* partition, uint32_t cluster); unsigned int _FAT_fat_freeClusterCount (PARTITION* partition); static inline sec_t _FAT_fat_clusterToSector (PARTITION* partition, uint32_t cluster) { - return (cluster >= CLUSTER_FIRST) ? - ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : + return (cluster >= CLUSTER_FIRST) ? + ((cluster - CLUSTER_FIRST) * (sec_t)partition->sectorsPerCluster) + partition->dataStart : partition->rootDirStart; } diff --git a/source/libfat/filetime.h b/source/libfat/filetime.h index 8ffd539a..3bfd8ed8 100644 --- a/source/libfat/filetime.h +++ b/source/libfat/filetime.h @@ -3,7 +3,7 @@ Conversion of file time and date values to various other types Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -26,8 +26,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __FILETIME_H -#define __FILETIME_H +#ifndef _FILETIME_H +#define _FILETIME_H #include "common.h" #include diff --git a/source/libfat/libfat.c b/source/libfat/libfat.c index 0b3cbd06..71af3689 100644 --- a/source/libfat/libfat.c +++ b/source/libfat/libfat.c @@ -36,7 +36,7 @@ #include "fatdir.h" #include "lock.h" #include "mem_allocate.h" -#include "disc_fat.h" +#include "disc.h" static const devoptab_t dotab_fat = { "fat", @@ -69,17 +69,17 @@ bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSec devoptab_t* devops; char* nameCopy; + if(!name || !interface) + return false; + if(!interface->startup()) return false; - if(!interface->isInserted()) { - interface->shutdown(); + if(!interface->isInserted()) return false; - } devops = _FAT_mem_allocate (sizeof(devoptab_t) + strlen(name) + 1); if (!devops) { - interface->shutdown(); return false; } // Use the space allocated at the end of the devoptab struct for storing the name @@ -89,7 +89,6 @@ bool fatMount (const char* name, const DISC_INTERFACE* interface, sec_t startSec partition = _FAT_partition_constructor (interface, cacheSize, SectorsPerPage, startSector); if (!partition) { _FAT_mem_free (devops); - interface->shutdown(); return false; } @@ -111,7 +110,9 @@ bool fatMountSimple (const char* name, const DISC_INTERFACE* interface) { void fatUnmount (const char* name) { devoptab_t *devops; PARTITION* partition; - const DISC_INTERFACE *disc; + + if(!name) + return; devops = (devoptab_t*)GetDeviceOpTab (name); if (!devops) { @@ -128,10 +129,8 @@ void fatUnmount (const char* name) { } partition = (PARTITION*)devops->deviceData; - disc = partition->disc; _FAT_partition_destructor (partition); _FAT_mem_free (devops); - disc->shutdown(); } bool fatInit (uint32_t cacheSize, bool setAsDefaultDevice) { @@ -194,4 +193,49 @@ bool fatInitDefault (void) { return fatInit (DEFAULT_CACHE_PAGES, true); } +void fatGetVolumeLabel (const char* name, char *label) { + devoptab_t *devops; + PARTITION* partition; + char *buf; + int namelen,i; + if(!name || !label) + return; + + namelen = strlen(name); + buf=(char*)_FAT_mem_allocate(sizeof(char)*namelen+2); + strcpy(buf,name); + + if (name[namelen-1] == '/') { + buf[namelen-1]='\0'; + namelen--; + } + + if (name[namelen-1] != ':') { + buf[namelen]=':'; + buf[namelen+1]='\0'; + } + + devops = (devoptab_t*)GetDeviceOpTab(buf); + + for(i=0;buf[i]!='\0' && buf[i]!=':';i++); + if (!devops || strncasecmp(buf,devops->name,i)) { + free(buf); + return; + } + + free(buf); + + // Perform a quick check to make sure we're dealing with a libfat controlled device + if (devops->open_r != dotab_fat.open_r) { + return; + } + + partition = (PARTITION*)devops->deviceData; + + if(!_FAT_directory_getVolumeLabel(partition, label)) { + strncpy(label,partition->label,11); + label[11]='\0'; + } + if(!strncmp(label, "NO NAME", 7)) label[0]='\0'; +} diff --git a/source/libfat/libfatversion.h b/source/libfat/libfatversion.h new file mode 100644 index 00000000..40c50463 --- /dev/null +++ b/source/libfat/libfatversion.h @@ -0,0 +1,10 @@ +#ifndef __LIBFATVERSION_H__ +#define __LIBFATVERSION_H__ + +#define _LIBFAT_MAJOR_ 1 +#define _LIBFAT_MINOR_ 0 +#define _LIBFAT_PATCH_ 7 + +#define _LIBFAT_STRING "libFAT Release 1.0.7" + +#endif // __LIBFATVERSION_H__ diff --git a/source/libfat/lock.h b/source/libfat/lock.h index 73b8902b..a93194c2 100644 --- a/source/libfat/lock.h +++ b/source/libfat/lock.h @@ -26,8 +26,8 @@ */ -#ifndef __LOCK_H -#define __LOCK_H +#ifndef _LOCK_H +#define _LOCK_H #include "common.h" diff --git a/source/libfat/mem_allocate.h b/source/libfat/mem_allocate.h index 2d38f9ce..451cf3b1 100644 --- a/source/libfat/mem_allocate.h +++ b/source/libfat/mem_allocate.h @@ -5,7 +5,7 @@ malloc is unavailable Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -28,8 +28,8 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __MEM_ALLOCATE_H_ -#define __MEM_ALLOCATE_H_ +#ifndef _MEM_ALLOCATE_H +#define _MEM_ALLOCATE_H #include @@ -38,7 +38,6 @@ static inline void* _FAT_mem_allocate (size_t size) { } static inline void* _FAT_mem_align (size_t size) { - return memalign (32, size); } diff --git a/source/libfat/partition.c b/source/libfat/partition.c index 955468de..dc1a29c9 100644 --- a/source/libfat/partition.c +++ b/source/libfat/partition.c @@ -205,11 +205,17 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach return NULL; } - _FAT_startSector = startSector; - // Init the partition lock _FAT_lock_init(&partition->lock); + _FAT_startSector = startSector; + + if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG))) + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT16_volumeLabel), 11); + else + strncpy(partition->label, (char*)(sectorBuffer + BPB_FAT32_volumeLabel), 11); + partition->label[11] = '\0'; + // Set partition's disc interface partition->disc = disc; diff --git a/source/libfat/partition.h b/source/libfat/partition.h index 5c9595e0..52e6648d 100644 --- a/source/libfat/partition.h +++ b/source/libfat/partition.h @@ -4,7 +4,7 @@ on various block devices. Copyright (c) 2006 Michael "Chishm" Chisholm - + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: @@ -27,11 +27,11 @@ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __PARTITION_H -#define __PARTITION_H +#ifndef _PARTITION_H +#define _PARTITION_H #include "common.h" -#include "fat_cache.h" +#include "cache.h" #include "lock.h" // Device name @@ -67,6 +67,7 @@ typedef struct { struct _FILE_STRUCT* firstOpenFile; // The start of a linked list of files mutex_t lock; // A lock for partition operations bool readOnly; // If this is set, then do not try writing to the disc + char label[12]; // Volume label } PARTITION; /* @@ -75,7 +76,7 @@ Mount the supplied device and return a pointer to the struct necessary to use it PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t SectorsPerPage, sec_t startSector); /* -Dismount the device and free all structures used. +Dismount the device and free all structures used. Will also attempt to synchronise all open files to disc. */ void _FAT_partition_destructor (PARTITION* partition); diff --git a/source/libntfs/acls.c b/source/libntfs/acls.c index 4bfd3652..76cc6ce5 100644 --- a/source/libntfs/acls.c +++ b/source/libntfs/acls.c @@ -563,7 +563,8 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) &((const char*)pacl)[offace]; acesz = le16_to_cpu(pace->size); if (((offace + acesz) > end) - || !ntfs_valid_sid(&pace->sid)) + || !ntfs_valid_sid(&pace->sid) + || ((ntfs_sid_size(&pace->sid) + 8) != (int)acesz)) ok = FALSE; offace += acesz; } @@ -614,7 +615,6 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) * old revision and no DACL though SE_DACL_PRESENT is set */ if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (ntfs_attr_size(securattr) <= attrsz) && (phead->revision == SECURITY_DESCRIPTOR_REVISION) && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) && ((offowner + 2) < attrsz) @@ -622,14 +622,15 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) && ((offgroup + 2) < attrsz) && (!offdacl || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offdacl < attrsz))) + && (offdacl+sizeof(ACL) < attrsz))) && (!offsacl || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE)) - && (offsacl < attrsz))) + && (offsacl+sizeof(ACL) < attrsz))) && !(phead->owner & const_cpu_to_le32(3)) && !(phead->group & const_cpu_to_le32(3)) && !(phead->dacl & const_cpu_to_le32(3)) && !(phead->sacl & const_cpu_to_le32(3)) + && (ntfs_attr_size(securattr) <= attrsz) && ntfs_valid_sid((const SID*)&securattr[offowner]) && ntfs_valid_sid((const SID*)&securattr[offgroup]) /* @@ -707,10 +708,12 @@ int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl, if (ntfs_same_sid(&pnewace->sid, ownersid)) { memcpy(&pnewace->sid, usid, usidsz); acesz = usidsz + 8; + pnewace->size = cpu_to_le16(acesz); } if (ntfs_same_sid(&pnewace->sid, groupsid)) { memcpy(&pnewace->sid, gsid, gsidsz); acesz = gsidsz + 8; + pnewace->size = cpu_to_le16(acesz); } if (pnewace->mask & GENERIC_ALL) { pnewace->mask &= ~GENERIC_ALL; @@ -2985,8 +2988,10 @@ static int build_std_permissions(const char *securattr, if (offdacl) { acecnt = le16_to_cpu(pacl->ace_count); offace = offdacl + sizeof(ACL); - } else + } else { acecnt = 0; + offace = 0; + } for (nace = 0; nace < acecnt; nace++) { pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; if (!(pace->flags & INHERIT_ONLY_ACE)) { @@ -3255,8 +3260,10 @@ static int build_ownadmin_permissions(const char *securattr, if (offdacl) { acecnt = le16_to_cpu(pacl->ace_count); offace = offdacl + sizeof(ACL); - } else + } else { acecnt = 0; + offace = 0; + } firstapply = TRUE; isforeign = 3; for (nace = 0; nace < acecnt; nace++) { diff --git a/source/libntfs/acls.h b/source/libntfs/acls.h index 13e5dbd1..8a83d32d 100644 --- a/source/libntfs/acls.h +++ b/source/libntfs/acls.h @@ -29,8 +29,6 @@ * should be moved to some config file */ -#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ -#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ #define BUFSZ 1024 /* buffer size to read mapping file */ #define MAPPINGFILE ".NTFS-3G/UserMapping" /* default mapping file */ #define LINESZ 120 /* maximum useful size of a mapping line */ diff --git a/source/libntfs/attrib.c b/source/libntfs/attrib.c index 41e4dcf0..123c9a91 100644 --- a/source/libntfs/attrib.c +++ b/source/libntfs/attrib.c @@ -44,6 +44,7 @@ #include #endif +#include "param.h" #include "compat.h" #include "attrib.h" #include "attrlist.h" @@ -64,8 +65,6 @@ #include "misc.h" #include "efs.h" -#define STANDARD_COMPRESSION_UNIT 4 - ntfschar AT_UNNAMED[] = { const_cpu_to_le16('\0') }; ntfschar STREAM_SDS[] = { const_cpu_to_le16('$'), const_cpu_to_le16('S'), @@ -463,9 +462,15 @@ ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, * inode (for named data streams). The compression mark * may change any time, the compression state can only * change when stream is wiped out. + * + * Also prevent compression on NTFS version < 3.0 + * or cluster size > 4K or compression is disabled */ a->flags &= ~ATTR_COMPRESSION_MASK; - if (na->ni->flags & FILE_ATTR_COMPRESSED) + if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE)) a->flags |= ATTR_IS_COMPRESSED; } @@ -606,6 +611,11 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", (unsigned long long)na->ni->mft_no, na->type); + /* avoid multiple full runlist mappings */ + if (NAttrFullyMapped(na)) { + ret = 0; + goto out; + } ctx = ntfs_attr_get_search_ctx(na->ni, NULL); if (!ctx) goto out; @@ -679,8 +689,10 @@ int ntfs_attr_map_whole_runlist(ntfs_attr *na) (long long)highest_vcn, (long long)last_vcn); goto err_out; } - if (errno == ENOENT) + if (errno == ENOENT) { + NAttrSetFullyMapped(na); ret = 0; + } err_out: ntfs_attr_put_search_ctx(ctx); out: @@ -1187,24 +1199,49 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, if ((na->data_flags & ATTR_COMPRESSION_MASK) && (need < na->compression_block_clusters)) { /* - * for a compressed file, be sure to allocate the full hole. - * We may need space to decompress existing compressed data. + * for a compressed file, be sure to allocate the full + * compression block, as we may need space to decompress + * existing compressed data. + * So allocate the space common to compression block + * and existing hole. */ - rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length, + VCN alloc_vcn; + + if ((from_vcn & -na->compression_block_clusters) <= (*rl)->vcn) + alloc_vcn = (*rl)->vcn; + else + alloc_vcn = from_vcn & -na->compression_block_clusters; + need = (alloc_vcn | (na->compression_block_clusters - 1)) + + 1 - alloc_vcn; + if (need > (*rl)->length) { + ntfs_log_error("Cannot allocate %lld clusters" + " within a hole of %lld\n", + (long long)need, + (long long)(*rl)->length); + errno = EIO; + goto err_out; + } + rlc = ntfs_cluster_alloc(vol, alloc_vcn, need, lcn_seek_from, DATA_ZONE); } else rlc = ntfs_cluster_alloc(vol, from_vcn, need, lcn_seek_from, DATA_ZONE); if (!rlc) goto err_out; + if (na->data_flags & (ATTR_COMPRESSION_MASK | ATTR_IS_SPARSE)) + na->compressed_size += need << vol->cluster_size_bits; *rl = ntfs_runlists_merge(na->rl, rlc); /* - * For a compressed attribute, we must be sure there is an - * available entry, so reserve it before it gets too late. + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. */ - if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) - *rl = ntfs_rl_extend(*rl,1); + if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) { + runlist_element *oldrl = na->rl; + na->rl = *rl; + *rl = ntfs_rl_extend(na,*rl,2); + if (!*rl) na->rl = oldrl; /* restore to original if failed */ + } if (!*rl) { eo = errno; ntfs_log_perror("Failed to merge runlists"); @@ -1215,8 +1252,9 @@ static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, errno = eo; goto err_out; } + na->unused_runs = 2; na->rl = *rl; - if (*update_from == -1) + if ((*update_from == -1) || (from_vcn < *update_from)) *update_from = from_vcn; *rl = ntfs_attr_find_vcn(na, cur_vcn); if (!*rl) { @@ -1266,6 +1304,314 @@ err_out: static int stuff_hole(ntfs_attr *na, const s64 pos); +/* + * Split an existing hole for overwriting with data + * The hole may have to be split into two or three parts, so + * that the overwritten part fits within a single compression block + * + * No cluster allocation is needed, this will be done later in + * standard hole filling, hence no need to reserve runs for + * future needs. + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * (or the full block, if it was a full hole) + * -1 if there were an error + */ + +static int split_compressed_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from) +{ + int compressed_part; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + + compressed_part + = na->compression_block_clusters; + /* reserve entries in runlist if we have to split */ + if (rl->length > na->compression_block_clusters) { + *prl = ntfs_rl_extend(na,*prl,2); + if (!*prl) { + compressed_part = -1; + } else { + rl = *prl; + na->unused_runs = 2; + } + } + if (*prl && (rl->length > na->compression_block_clusters)) { + /* + * Locate the update part relative to beginning of + * current run + */ + int beginwrite = (pos >> cluster_size_bits) - rl->vcn; + s32 endblock = (((pos + count - 1) >> cluster_size_bits) + | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + + compressed_part = na->compression_block_clusters + - (rl->length & (na->compression_block_clusters - 1)); + if ((beginwrite + compressed_part) >= na->compression_block_clusters) + compressed_part = na->compression_block_clusters; + /* + * if the run ends beyond end of needed block + * we have to split the run + */ + if (endblock < rl[0].length) { + runlist_element *xrl; + int n; + + /* + * we have to split into three parts if the run + * does not end within the first compression block. + * This means the hole begins before the + * compression block. + */ + if (endblock > na->compression_block_clusters) { + if (na->unused_runs < 2) { +ntfs_log_error("No free run, case 1\n"); + } + na->unused_runs -= 2; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[2] = *xrl; + xrl--; + } while (xrl != rl); + rl[1].length = na->compression_block_clusters; + rl[2].length = rl[0].length - endblock; + rl[0].length = endblock + - na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[2].lcn = LCN_HOLE; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[2].vcn = rl[1].vcn + + na->compression_block_clusters; + rl = ++(*prl); + } else { + /* + * split into two parts and use the + * first one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 2\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + if (beginwrite < endblock) { + /* we will write into the first part of hole */ + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + } else { + /* we will write into the second part of hole */ +// impossible ? + rl[1].length = rl[0].length - endblock; + rl[0].length = endblock; + rl[1].vcn = rl[0].vcn + rl[0].length; + rl[1].lcn = LCN_HOLE; + rl = ++(*prl); + } + } + } else { + if (rl[1].length) { + runlist_element *xrl; + int n; + + /* + * split into two parts and use the + * last one + */ + if (!na->unused_runs) { +ntfs_log_error("No free run, case 4\n"); + } + na->unused_runs--; + xrl = rl; + n = 0; + while (xrl->length) { + xrl++; + n++; + } + do { + xrl[1] = *xrl; + xrl--; + } while (xrl != rl); + } else { + rl[2].lcn = rl[1].lcn; + rl[2].vcn = rl[1].vcn; + rl[2].length = rl[1].length; + } + rl[1].vcn -= na->compression_block_clusters; + rl[1].lcn = LCN_HOLE; + rl[1].length = na->compression_block_clusters; + rl[0].length -= na->compression_block_clusters; + if (pos >= (rl[1].vcn << cluster_size_bits)) { + rl = ++(*prl); + } + } + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + } + return (compressed_part); +} + +/* + * Borrow space from adjacent hole for appending data + * The hole may have to be split so that the end of hole is not + * affected by cluster allocation and overwriting + * Cluster allocation is needed for the overwritten compression block + * + * Must always leave two unused entries in the runlist + * + * Returns the number of clusters with existing compressed data + * in the compression block to be written to + * -1 if there were an error + */ + +static int borrow_from_hole(ntfs_attr *na, runlist_element **prl, + s64 pos, s64 count, VCN *update_from, BOOL wasnonresident) +{ + int compressed_part = 0; + int cluster_size_bits = na->ni->vol->cluster_size_bits; + runlist_element *rl = *prl; + s32 endblock; + long long allocated; + runlist_element *zrl; + int irl; + BOOL undecided; + BOOL nothole; + + /* check whether the compression block is fully allocated */ + endblock = (((pos + count - 1) >> cluster_size_bits) | (na->compression_block_clusters - 1)) + 1 - rl->vcn; + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + + undecided = (allocated < endblock) && (zrl->lcn == LCN_RL_NOT_MAPPED); + nothole = (allocated >= endblock) || (zrl->lcn != LCN_HOLE); + + if (undecided || nothole) { + runlist_element *orl = na->rl; + s64 olcn = (*prl)->lcn; + /* + * Map the full runlist (needed to compute the + * compressed size), unless the runlist has not + * yet been created (data just made non-resident) + */ + irl = *prl - na->rl; + if (!NAttrBeingNonResident(na) + && ntfs_attr_map_whole_runlist(na)) { + rl = (runlist_element*)NULL; + } else { + /* + * Mapping the runlist may cause its relocation, + * and relocation may be at the same place with + * relocated contents. + * Have to find the current run again when this + * happens. + */ + if ((na->rl != orl) || ((*prl)->lcn != olcn)) { + zrl = &na->rl[irl]; + while (zrl->length && (zrl->lcn != olcn)) + zrl++; + *prl = zrl; + } + if (!(*prl)->length) { + ntfs_log_error("Mapped run not found," + " inode %lld lcn 0x%llx\n", + (long long)na->ni->mft_no, + (long long)olcn); + rl = (runlist_element*)NULL; + } else { + rl = ntfs_rl_extend(na,*prl,2); + na->unused_runs = 2; + } + } + *prl = rl; + if (rl && undecided) { + allocated = 0; + zrl = rl; + irl = 0; + while (zrl->length && (zrl->lcn >= 0) + && (allocated < endblock)) { + allocated += zrl->length; + zrl++; + irl++; + } + } + } + /* + * compression block not fully allocated and followed + * by a hole : we must allocate in the hole. + */ + if (rl && (allocated < endblock) && (zrl->lcn == LCN_HOLE)) { + s64 xofs; + + /* + * split the hole if not fully needed + */ + if ((allocated + zrl->length) > endblock) { + runlist_element *xrl; + + *prl = ntfs_rl_extend(na,*prl,1); + if (*prl) { + /* beware : rl was reallocated */ + rl = *prl; + zrl = &rl[irl]; + na->unused_runs = 0; + xrl = zrl; + while (xrl->length) xrl++; + do { + xrl[1] = *xrl; + } while (xrl-- != zrl); + zrl->length = endblock - allocated; + zrl[1].length -= zrl->length; + zrl[1].vcn = zrl->vcn + zrl->length; + } + } + if (*prl) { + if (wasnonresident) + compressed_part = na->compression_block_clusters + - zrl->length; + xofs = 0; + if (ntfs_attr_fill_hole(na, + zrl->length << cluster_size_bits, + &xofs, &zrl, update_from)) + compressed_part = -1; + else { + /* go back to initial cluster, now reallocated */ + while (zrl->vcn > (pos >> cluster_size_bits)) + zrl--; + *prl = zrl; + } + } + } + if (!*prl) { + ntfs_log_error("No elements to borrow from a hole\n"); + compressed_part = -1; + } else + if ((*update_from == -1) || ((*prl)->vcn < *update_from)) + *update_from = (*prl)->vcn; + return (compressed_part); +} + /** * ntfs_attr_pwrite - positioned write to an ntfs attribute * @na: ntfs attribute to write to @@ -1301,9 +1647,9 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) unsigned int undo_initialized_size : 1; unsigned int undo_data_size : 1; } need_to = { 0, 0 }; - BOOL makingnonresident = FALSE; BOOL wasnonresident = FALSE; BOOL compressed; + BOOL updatemap; ntfs_log_enter("Entering for inode %lld, attr 0x%x, pos 0x%llx, count " "0x%llx.\n", (long long)na->ni->mft_no, na->type, @@ -1317,6 +1663,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) vol = na->ni->vol; compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); + na->unused_runs = 0; /* prepare overflow checks */ /* * Encrypted attributes are only supported in raw mode. We return * access denied, which is what Windows NT4 does, too. @@ -1338,23 +1685,14 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto errno_set; /* If this is a compressed attribute it needs special treatment. */ wasnonresident = NAttrNonResident(na) != 0; - makingnonresident = wasnonresident /* yes : already changed */ - && !pos && (count == na->initialized_size); /* - * Writing to compressed files is currently restricted - * to appending data. However we have to accept - * recursive write calls to make the attribute non resident. - * These are writing at position 0 up to initialized_size. - * Compression is also restricted to data streams. - * Only ATTR_IS_COMPRESSED compression mode is supported. + * Compression is restricted to data streams and + * only ATTR_IS_COMPRESSED compression mode is supported. */ if (compressed && ((na->type != AT_DATA) || ((na->data_flags & ATTR_COMPRESSION_MASK) - != ATTR_IS_COMPRESSED) - || ((pos != na->initialized_size) - && (pos || (count != na->initialized_size))))) { - // TODO: Implement writing compressed attributes! (AIA) + != ATTR_IS_COMPRESSED))) { errno = EOPNOTSUPP; goto errno_set; } @@ -1384,7 +1722,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * so truncate the requested count if needed (big buffers). */ if (compressed) { - fullcount = na->data_size - pos; + fullcount = (pos | (na->compression_block_size - 1)) + 1 - pos; if (count > fullcount) count = fullcount; } @@ -1428,6 +1766,7 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } /* Handle writes beyond initialized_size. */ + if (pos + count > na->initialized_size) { if (ntfs_attr_map_whole_runlist(na)) goto err_out; @@ -1438,9 +1777,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) * before it gets too late. */ if (compressed) { - na->rl = ntfs_rl_extend(na->rl,2); + na->rl = ntfs_rl_extend(na,na->rl,2); if (!na->rl) goto err_out; + na->unused_runs = 2; } /* Set initialized_size to @pos + @count. */ ctx = ntfs_attr_get_search_ctx(na->ni, NULL); @@ -1458,8 +1798,10 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) ctx->attr->initialized_size = cpu_to_sle64(pos + count); /* fix data_size for compressed files */ - if (compressed) + if (compressed) { + na->data_size = pos + count; ctx->attr->data_size = ctx->attr->initialized_size; + } if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, ctx->mrec)) { /* @@ -1473,6 +1815,19 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) goto err_out; } na->initialized_size = pos + count; +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + if ((compressed || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size = na->compressed_size; + else + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } +#endif ntfs_attr_put_search_ctx(ctx); ctx = NULL; /* @@ -1497,7 +1852,6 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) } goto err_out; } - ofs = pos - (rl->vcn << vol->cluster_size_bits); /* * Determine if there is compressed data in the current * compression block (when appending to an existing file). @@ -1518,49 +1872,51 @@ s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) if ((rl->lcn == (LCN)LCN_HOLE) && wasnonresident) { if (rl->length < na->compression_block_clusters) + /* + * the needed block is in a hole smaller + * than the compression block : we can use + * it fully + */ compressed_part = na->compression_block_clusters - rl->length; else { - compressed_part - = na->compression_block_clusters; - if (rl->length > na->compression_block_clusters) { - rl[2].lcn = rl[1].lcn; - rl[2].vcn = rl[1].vcn; - rl[2].length = rl[1].length; - rl[1].vcn -= compressed_part; - rl[1].lcn = LCN_HOLE; - rl[1].length = compressed_part; - rl[0].length -= compressed_part; - ofs -= rl->length << vol->cluster_size_bits; - rl++; - } + /* + * the needed block is in a hole bigger + * than the compression block : we must + * split the hole and use it partially + */ + compressed_part = split_compressed_hole(na, + &rl, pos, count, &update_from); } - /* normal hole filling will do later */ - } else - if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) { - s64 xofs; + } else { + if (rl->lcn >= 0) { + /* + * the needed block contains data, make + * sure the full compression block is + * allocated. Borrow from hole if needed + */ + compressed_part = borrow_from_hole(na, + &rl, pos, count, &update_from, + wasnonresident); + } + } - if (wasnonresident) - compressed_part = na->compression_block_clusters - - rl[1].length; - rl++; - xofs = 0; - if (ntfs_attr_fill_hole(na, - rl->length << vol->cluster_size_bits, - &xofs, &rl, &update_from)) - goto err_out; - /* the fist allocated cluster was not merged */ - if (!xofs) - rl--; - } + if (compressed_part < 0) + goto err_out; + + /* just making non-resident, so not yet compressed */ + if (NAttrBeingNonResident(na) + && (compressed_part < na->compression_block_clusters)) + compressed_part = 0; } + ofs = pos - (rl->vcn << vol->cluster_size_bits); /* * Scatter the data from the linear data buffer to the volume. Note, a * partial final vcn is taken care of by the @count capping of write * length. */ - for (hole_end = 0; count; rl++, ofs = 0, hole_end = 0) { + for (hole_end = 0; count; rl++, ofs = 0) { if (rl->lcn == LCN_RL_NOT_MAPPED) { rl = ntfs_attr_find_vcn(na, rl->vcn); if (!rl) { @@ -1641,7 +1997,8 @@ retry: if (compressed) { written = ntfs_compressed_pwrite(na, rl, wpos, ofs, to_write, - rounding, b, compressed_part); + rounding, cb, compressed_part, + &update_from); } else { written = ntfs_pwrite(vol->dev, wpos, rounding, cb); @@ -1654,7 +2011,8 @@ retry: if (compressed) { written = ntfs_compressed_pwrite(na, rl, wpos, ofs, to_write, - to_write, b, compressed_part); + to_write, b, compressed_part, + &update_from); } else written = ntfs_pwrite(vol->dev, wpos, to_write, b); @@ -1682,10 +2040,17 @@ retry: done: if (ctx) ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - if ((update_from != -1) - || (compressed && !makingnonresident)) - if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { + /* + * Update mapping pairs if needed. + * For a compressed file, we try to make a partial update + * of the mapping list. This makes a difference only if + * inode extents were needed. + */ + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + if (ntfs_attr_update_mapping_pairs(na, + (update_from < 0 ? 0 : update_from))) { /* * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. @@ -1749,8 +2114,10 @@ err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - if (update_from != -1) - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); + updatemap = (compressed + ? NAttrFullyMapped(na) != 0 : update_from != -1); + if (updatemap) + ntfs_attr_update_mapping_pairs(na, 0); /* Restore original data_size if needed. */ if (need_to.undo_data_size && ntfs_attr_truncate(na, old_data_size)) ntfs_log_perror("Failed to restore data_size"); @@ -1762,7 +2129,8 @@ errno_set: int ntfs_attr_pclose(ntfs_attr *na) { - s64 written, ofs; + s64 ofs; + int failed; BOOL ok = TRUE; VCN update_from = -1; ntfs_volume *vol; @@ -1782,6 +2150,7 @@ int ntfs_attr_pclose(ntfs_attr *na) goto errno_set; } vol = na->ni->vol; + na->unused_runs = 0; compressed = (na->data_flags & ATTR_COMPRESSION_MASK) != const_cpu_to_le16(0); /* @@ -1797,15 +2166,25 @@ int ntfs_attr_pclose(ntfs_attr *na) if (!compressed || !NAttrNonResident(na)) goto out; + /* safety check : no recursion on close */ + if (NAttrComprClosing(na)) { + errno = EIO; + ntfs_log_error("Bad ntfs_attr_pclose" + " recursion on inode %lld\n", + (long long)na->ni->mft_no); + goto out; + } + NAttrSetComprClosing(na); /* - * For a compressed attribute, we must be sure there is an - * available entry, so reserve it before it gets too late. + * For a compressed attribute, we must be sure there are two + * available entries, so reserve them before it gets too late. */ if (ntfs_attr_map_whole_runlist(na)) goto err_out; - na->rl = ntfs_rl_extend(na->rl,1); + na->rl = ntfs_rl_extend(na,na->rl,2); if (!na->rl) goto err_out; + na->unused_runs = 2; /* Find the runlist element containing the terminal vcn. */ rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); if (!rl) { @@ -1826,10 +2205,16 @@ int ntfs_attr_pclose(ntfs_attr *na) * length. */ compressed_part = 0; - if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) - compressed_part - = na->compression_block_clusters - rl[1].length; - else + if (rl->lcn >= 0) { + runlist_element *xrl; + + xrl = rl; + do { + xrl++; + } while (xrl->lcn >= 0); + compressed_part = (-xrl->length) + & (na->compression_block_clusters - 1); + } else if (rl->lcn == (LCN)LCN_HOLE) { if (rl->length < na->compression_block_clusters) compressed_part @@ -1883,26 +2268,32 @@ int ntfs_attr_pclose(ntfs_attr *na) } retry: - written = 0; + failed = 0; + if (update_from < 0) update_from = 0; if (!NVolReadOnly(vol)) { - - written = ntfs_compressed_close(na, rl, ofs); - /* If everything ok, update progress counters and continue. */ - if (!written) - goto done; + failed = ntfs_compressed_close(na, rl, ofs, &update_from); +#if CACHE_NIDATA_SIZE + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->compressed_size; + set_nino_flag(na->ni,KnownSize); + } +#endif } + if (failed) { /* If the syscall was interrupted, try again. */ - if (written == (s64)-1 && errno == EINTR) - goto retry; - if (!written) - errno = EIO; - goto rl_err_out; - -done: + if (errno == EINTR) + goto retry; + else + goto rl_err_out; + } if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { + if (NAttrFullyMapped(na)) + if (ntfs_attr_update_mapping_pairs(na, update_from)) { /* * FIXME: trying to recover by goto rl_err_out; * could cause driver hang by infinite looping. @@ -1925,7 +2316,8 @@ err_out: if (ctx) ntfs_attr_put_search_ctx(ctx); /* Update mapping pairs if needed. */ - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); + if (NAttrFullyMapped(na)) + ntfs_attr_update_mapping_pairs(na, 0); errno = eo; errno_set: ok = FALSE; @@ -2195,38 +2587,24 @@ static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, errno = ENOENT; return -1; } - } else if (name && !ntfs_names_are_equal(name, name_len, - (ntfschar*)((char*)a + le16_to_cpu(a->name_offset)), - a->name_length, ic, upcase, upcase_len)) { + } else { register int rc; - - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, IGNORE_CASE, - upcase, upcase_len); - /* - * If @name collates before a->name, there is no - * matching attribute. - */ - if (rc == -1) { - errno = ENOENT; - return -1; - } + if (name && ((rc = ntfs_names_full_collate(name, + name_len, (ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, ic, + upcase, upcase_len)))) { + /* + * If @name collates before a->name, + * there is no matching attribute. + */ + if (rc < 0) { + errno = ENOENT; + return -1; + } /* If the strings are not equal, continue search. */ - if (rc) - continue; - rc = ntfs_names_collate(name, name_len, - (ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, 1, CASE_SENSITIVE, - upcase, upcase_len); - if (rc == -1) { - errno = ENOENT; - return -1; + continue; } - if (rc) - continue; } /* * The names match or @name not present and attribute is @@ -2506,38 +2884,22 @@ find_attr_list_attr: if (name == AT_UNNAMED) { if (al_name_len) goto not_found; - } else if (name && !ntfs_names_are_equal(al_name, al_name_len, - name, name_len, ic, vol->upcase, - vol->upcase_len)) { - register int rc; + } else { + int rc; - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, IGNORE_CASE, - vol->upcase, vol->upcase_len); - /* - * If @name collates before al_name, there is no - * matching attribute. - */ - if (rc == -1) - goto not_found; - /* If the strings are not equal, continue search. */ - if (rc) - continue; - /* - * FIXME: Reverse engineering showed 0, IGNORE_CASE but - * that is inconsistent with ntfs_attr_find(). The - * subsequent rc checks were also different. Perhaps I - * made a mistake in one of the two. Need to recheck - * which is correct or at least see what is going - * on... (AIA) - */ - rc = ntfs_names_collate(name, name_len, al_name, - al_name_len, 1, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); - if (rc == -1) - goto not_found; - if (rc) + if (name && ((rc = ntfs_names_full_collate(name, + name_len, al_name, al_name_len, ic, + vol->upcase, vol->upcase_len)))) { + + /* + * If @name collates before al_name, + * there is no matching attribute. + */ + if (rc < 0) + goto not_found; + /* If the strings are not equal, continue search. */ continue; + } } /* * The names match or @name not present and attribute is @@ -3280,9 +3642,12 @@ int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, goto put_err_out; } } - if (type == AT_DATA && name == AT_UNNAMED) { + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? type == AT_INDEX_ROOT && name == NTFS_INDEX_I30 + : type == AT_DATA && name == AT_UNNAMED) { ni->data_size = size; ni->allocated_size = (size + 7) & ~7; + set_nino_flag(ni,KnownSize); } ntfs_inode_mark_dirty(ni); ntfs_attr_put_search_ctx(ctx); @@ -3454,7 +3819,6 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) { ntfs_inode *base_ni, *ni; ATTR_TYPES type; - int err; if (!ctx || !ctx->ntfs_ino || !ctx->mrec || !ctx->attr) { errno = EINVAL; @@ -3479,7 +3843,7 @@ int ntfs_attr_record_rm(ntfs_attr_search_ctx *ctx) if (ntfs_attrlist_entry_add(ni, ctx->attr)) ntfs_log_trace("Rollback failed. Leaving inconstant " "metadata.\n"); - err = EIO; + errno = EIO; return -1; } ntfs_inode_mark_dirty(ni); @@ -3715,6 +4079,9 @@ int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, add_attr_record: if ((ni->flags & FILE_ATTR_COMPRESSED) + && (ni->vol->major_ver >= 3) + && NVolCompression(ni->vol) + && (ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && ((type == AT_DATA) || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) data_flags = ATTR_IS_COMPRESSED; @@ -4199,6 +4566,13 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, - 1) & ~(vol->cluster_size - 1); if (new_allocated_size > 0) { + if ((a->flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) { + /* must allocate full compression blocks */ + new_allocated_size = ((new_allocated_size - 1) + | ((1L << (STANDARD_COMPRESSION_UNIT + + vol->cluster_size_bits)) - 1)) + 1; + } /* Start by allocating clusters to hold the attribute value. */ rl = ntfs_cluster_alloc(vol, 0, new_allocated_size >> vol->cluster_size_bits, -1, DATA_ZONE); @@ -4211,6 +4585,7 @@ int ntfs_attr_make_non_resident(ntfs_attr *na, * we can use ntfs_attr_pwrite(). */ NAttrSetNonResident(na); + NAttrSetBeingNonResident(na); na->rl = rl; na->allocated_size = new_allocated_size; na->data_size = na->initialized_size = le32_to_cpu(a->value_length); @@ -4345,6 +4720,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); * @newsize: new size (in bytes) to which to resize the attribute * * Change the size of a resident, open ntfs attribute @na to @newsize bytes. + * Can also be used to force an attribute non-resident. In this case, the + * size cannot be changed. * * On success return 0 * On error return values are: @@ -4355,7 +4732,8 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); * ERANGE - @newsize is not valid for the attribute type of @na. * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST. */ -static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) +static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize, + BOOL force_non_resident) { ntfs_attr_search_ctx *ctx; ntfs_volume *vol; @@ -4393,7 +4771,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) * attribute non-resident if the attribute type supports it. If it is * smaller we can go ahead and attempt the resize. */ - if (newsize < vol->mft_record_size) { + if ((newsize < vol->mft_record_size) && !force_non_resident) { /* Perform the resize of the attribute record. */ if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, newsize))) { @@ -4403,10 +4781,21 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) if ((na->data_flags & ATTR_COMPRESSION_MASK) || NAttrSparse(na)) na->compressed_size = na->allocated_size; - if (na->type == AT_DATA && na->name == AT_UNNAMED) { + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY + ? na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30 + : na->type == AT_DATA && na->name == AT_UNNAMED) { na->ni->data_size = na->data_size; - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty(na->ni); + if (((na->data_flags & ATTR_COMPRESSION_MASK) + || NAttrSparse(na)) + && NAttrNonResident(na)) + na->ni->allocated_size + = na->compressed_size; + else + na->ni->allocated_size + = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + if (na->type == AT_DATA) + NInoFileNameSetDirty(na->ni); } goto resize_done; } @@ -4422,6 +4811,21 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) if (!ntfs_attr_make_non_resident(na, ctx)) { ntfs_inode_mark_dirty(ctx->ntfs_ino); ntfs_attr_put_search_ctx(ctx); + /* + * do not truncate when forcing non-resident, this + * could cause the attribute to be made resident again, + * so size changes are not allowed. + */ + if (force_non_resident) { + ret = 0; + if (newsize != na->data_size) { + ntfs_log_error("Cannot change size when" + " forcing non-resident\n"); + errno = EIO; + ret = STATUS_ERROR; + } + return (ret); + } /* Resize non-resident attribute */ return ntfs_attr_truncate(na, newsize); } else if (errno != ENOSPC && errno != EPERM) { @@ -4460,10 +4864,17 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_close(tna); continue; } + if (((tna->data_flags & ATTR_COMPRESSION_MASK) + == ATTR_IS_COMPRESSED) + && ntfs_attr_pclose(tna)) { + err = errno; + ntfs_attr_close(tna); + goto put_err_out; + } ntfs_inode_mark_dirty(tna->ni); ntfs_attr_close(tna); ntfs_attr_put_search_ctx(ctx); - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* Check whether error occurred. */ if (errno != ENOENT) { @@ -4483,7 +4894,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_log_perror("Could not free space in MFT record"); return -1; } - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* @@ -4522,7 +4933,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_put_search_ctx(ctx); if (ntfs_inode_add_attrlist(ni)) return -1; - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); } /* Allocate new mft record. */ ni = ntfs_mft_record_alloc(vol, ni); @@ -4543,7 +4954,7 @@ static int ntfs_resident_attr_resize_i(ntfs_attr *na, const s64 newsize) ntfs_attr_put_search_ctx(ctx); /* Try to perform resize once again. */ - return ntfs_resident_attr_resize(na, newsize); + return ntfs_resident_attr_resize_i(na, newsize, force_non_resident); resize_done: /* @@ -4564,11 +4975,39 @@ static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize) int ret; ntfs_log_enter("Entering\n"); - ret = ntfs_resident_attr_resize_i(na, newsize); + ret = ntfs_resident_attr_resize_i(na, newsize, FALSE); ntfs_log_leave("\n"); return ret; } +/* + * Force an attribute to be made non-resident without + * changing its size. + * + * This is particularly needed when the attribute has no data, + * as the non-resident variant requires more space in the MFT + * record, and may imply expelling some other attribute. + * + * As a consequence the existing ntfs_attr_search_ctx's have to + * be closed or reinitialized. + * + * returns 0 if successful, + * < 0 if failed, with errno telling why + */ + +int ntfs_attr_force_non_resident(ntfs_attr *na) +{ + int res; + + res = ntfs_resident_attr_resize_i(na, na->data_size, TRUE); + if (!res && !NAttrNonResident(na)) { + res = -1; + errno = EIO; + ntfs_log_error("Failed to force non-resident\n"); + } + return (res); +} + /** * ntfs_attr_make_resident - convert a non-resident to a resident attribute * @na: open ntfs attribute to make resident @@ -4602,7 +5041,7 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) if (sle64_to_cpu(a->lowest_vcn)) { ntfs_log_trace("Eeek! Should be called for the first extent of the " "attribute. Aborting...\n"); - err = EINVAL; + errno = EINVAL; return -1; } @@ -4679,6 +5118,9 @@ static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) */ if (!na->data_size && (na->type == AT_DATA) + && (na->ni->vol->major_ver >= 3) + && NVolCompression(na->ni->vol) + && (na->ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && (na->ni->flags & FILE_ATTR_COMPRESSED)) { a->flags |= ATTR_IS_COMPRESSED; na->data_flags = a->flags; @@ -4851,7 +5293,7 @@ static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, * allocated size in the index. */ if (na->type == AT_DATA && na->name == AT_UNNAMED) { - if (sparse) + if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) na->ni->allocated_size = na->compressed_size; else na->ni->allocated_size = na->allocated_size; @@ -4878,9 +5320,10 @@ static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) const runlist_element *stop_rl; int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; BOOL finished_build; + BOOL first_updated = FALSE; retry: - if (!na || !na->rl || from_vcn) { + if (!na || !na->rl) { errno = EINVAL; ntfs_log_perror("%s: na=%p", __FUNCTION__, na); return -1; @@ -4912,6 +5355,8 @@ retry: CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { a = ctx->attr; m = ctx->mrec; + if (!a->lowest_vcn) + first_updated = TRUE; /* * If runlist is updating not from the beginning, then set * @stop_vcn properly, i.e. to the lowest vcn of record that @@ -5061,6 +5506,34 @@ retry: ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); goto put_err_out; } + /* + * If the base extent was skipped in the above process, + * we still may have to update the sizes. + */ + if (!first_updated) { + le16 spcomp; + + ntfs_attr_reinit_search_ctx(ctx); + if (!ntfs_attr_lookup(na->type, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + a = ctx->attr; + a->allocated_size = cpu_to_sle64(na->allocated_size); + spcomp = na->data_flags + & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); + if (spcomp) + a->compressed_size = cpu_to_sle64(na->compressed_size); + if ((na->type == AT_DATA) && (na->name == AT_UNNAMED)) { + na->ni->allocated_size + = (spcomp + ? na->compressed_size + : na->allocated_size); + NInoFileNameSetDirty(na->ni); + } + } else { + ntfs_log_error("Failed to update sizes in base extent\n"); + goto put_err_out; + } + } /* Deallocate not used attribute extents and return with success. */ if (finished_build) { @@ -5238,8 +5711,19 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) } /* The first cluster outside the new allocation. */ - first_free_vcn = (newsize + vol->cluster_size - 1) >> - vol->cluster_size_bits; + if (na->data_flags & ATTR_COMPRESSION_MASK) + /* + * For compressed files we must keep full compressions blocks, + * but currently we do not decompress/recompress the last + * block to truncate the data, so we may leave more allocated + * clusters than really needed. + */ + first_free_vcn = (((newsize - 1) + | (na->compression_block_size - 1)) + 1) + >> vol->cluster_size_bits; + else + first_free_vcn = (newsize + vol->cluster_size - 1) >> + vol->cluster_size_bits; /* * Compare the new allocation with the old one and only deallocate * clusters if there is a change. @@ -5305,9 +5789,17 @@ static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) ctx->attr->initialized_size = cpu_to_sle64(newsize); } /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } } /* If the attribute now has zero size, make it resident. */ @@ -5496,9 +5988,17 @@ static int ntfs_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) na->data_size = newsize; ctx->attr->data_size = cpu_to_sle64(newsize); /* Update data size in the index. */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - na->ni->data_size = na->data_size; - NInoFileNameSetDirty(na->ni); + if (na->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { + if (na->type == AT_INDEX_ROOT && na->name == NTFS_INDEX_I30) { + na->ni->data_size = na->data_size; + na->ni->allocated_size = na->allocated_size; + set_nino_flag(na->ni,KnownSize); + } + } else { + if (na->type == AT_DATA && na->name == AT_UNNAMED) { + na->ni->data_size = na->data_size; + NInoFileNameSetDirty(na->ni); + } } /* Set the inode dirty so it is written out later. */ ntfs_inode_mark_dirty(ctx->ntfs_ino); @@ -5598,7 +6098,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) */ if (na->data_flags & ATTR_IS_ENCRYPTED) { errno = EACCES; - ntfs_log_info("Failed to truncate encrypted attribute"); + ntfs_log_trace("Cannot truncate encrypted attribute\n"); goto out; } /* @@ -5612,8 +6112,7 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) != const_cpu_to_le16(0); if (compressed && NAttrNonResident(na) - && (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED) - || (newsize && (newsize < na->data_size)))) { + && ((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED)) { errno = EOPNOTSUPP; ntfs_log_perror("Failed to truncate compressed attribute"); goto out; @@ -5621,16 +6120,16 @@ int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) if (NAttrNonResident(na)) { /* * For compressed data, the last block must be fully - * allocated, and we do not known the size of compression + * allocated, and we do not know the size of compression * block until the attribute has been made non-resident. * Moreover we can only process a single compression * block at a time (from where we are about to write), * so we silently do not allocate more. * - * Note : do not request truncate on compressed files + * Note : do not request upsizing of compressed files * unless being able to face the consequences ! */ - if (compressed && newsize) + if (compressed && newsize && (newsize > na->data_size)) fullsize = (na->initialized_size | (na->compression_block_size - 1)) + 1; else diff --git a/source/libntfs/attrib.h b/source/libntfs/attrib.h index bcdb0117..43ab7f53 100644 --- a/source/libntfs/attrib.h +++ b/source/libntfs/attrib.h @@ -189,6 +189,7 @@ struct _ntfs_attr { u32 compression_block_size; u8 compression_block_size_bits; u8 compression_block_clusters; + s8 unused_runs; /* pre-reserved entries available */ }; /** @@ -198,6 +199,9 @@ struct _ntfs_attr { typedef enum { NA_Initialized, /* 1: structure is initialized. */ NA_NonResident, /* 1: Attribute is not resident. */ + NA_BeingNonResident, /* 1: Attribute is being made not resident. */ + NA_FullyMapped, /* 1: Attribute has been fully mapped */ + NA_ComprClosing, /* 1: Compressed attribute is being closed */ } ntfs_attr_state_bits; #define test_nattr_flag(na, flag) test_bit(NA_##flag, (na)->state) @@ -212,6 +216,18 @@ typedef enum { #define NAttrSetNonResident(na) set_nattr_flag(na, NonResident) #define NAttrClearNonResident(na) clear_nattr_flag(na, NonResident) +#define NAttrBeingNonResident(na) test_nattr_flag(na, BeingNonResident) +#define NAttrSetBeingNonResident(na) set_nattr_flag(na, BeingNonResident) +#define NAttrClearBeingNonResident(na) clear_nattr_flag(na, BeingNonResident) + +#define NAttrFullyMapped(na) test_nattr_flag(na, FullyMapped) +#define NAttrSetFullyMapped(na) set_nattr_flag(na, FullyMapped) +#define NAttrClearFullyMapped(na) clear_nattr_flag(na, FullyMapped) + +#define NAttrComprClosing(na) test_nattr_flag(na, ComprClosing) +#define NAttrSetComprClosing(na) set_nattr_flag(na, ComprClosing) +#define NAttrClearComprClosing(na) clear_nattr_flag(na, ComprClosing) + #define GenNAttrIno(func_name, flag) \ extern int NAttr##func_name(ntfs_attr *na); \ extern void NAttrSet##func_name(ntfs_attr *na); \ @@ -288,6 +304,7 @@ extern int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type); int ntfs_attr_make_non_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx); +int ntfs_attr_force_non_resident(ntfs_attr *na); extern int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size); extern int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, diff --git a/source/libntfs/attrib_frag.c b/source/libntfs/attrib_frag.c index 7f5a659e..5c3c4a1d 100644 --- a/source/libntfs/attrib_frag.c +++ b/source/libntfs/attrib_frag.c @@ -818,7 +818,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 s64 br, to_read, ofs, total, total2, max_read, max_init; ntfs_volume *vol; runlist_element *rl; -// u16 efs_padding_length; + //u16 efs_padding_length; /* Sanity checking arguments is done in ntfs_attr_pread(). */ diff --git a/source/libntfs/cache.c b/source/libntfs/cache.c index 6455b1f8..dd147672 100644 --- a/source/libntfs/cache.c +++ b/source/libntfs/cache.c @@ -1,378 +1,609 @@ -/* - cache.c - The cache is not visible to the user. It should be flushed - when any file is closed or changes are made to the filesystem. +/** + * cache.c : deal with LRU caches + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ - This cache implements a least-used-page replacement policy. This will - distribute sectors evenly over the pages, so if less than the maximum - pages are used at once, they should all eventually remain in the cache. - This also has the benefit of throwing out old sectors, so as not to keep - too many stale pages around. - - Copyright (c) 2006 Michael "Chishm" Chisholm - Copyright (c) 2009 shareese, rodries - Copyright (c) 2010 Dimok - - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_STRING_H #include -#include +#endif -//#include "common.h" +#include "types.h" +#include "security.h" #include "cache.h" -#include "bit_ops.h" -//#include "disc.h" +#include "misc.h" +#include "logging.h" -#include "mem_allocate.h" -#include -//#include "bit_ops.h" -//#include "file_allocation_table.h" +/* + * General functions to deal with LRU caches + * + * The cached data have to be organized in a structure in which + * the first fields must follow a mandatory pattern and further + * fields may contain any fixed size data. They are stored in an + * LRU list. + * + * A compare function must be provided for finding a wanted entry + * in the cache. Another function may be provided for invalidating + * an entry to facilitate multiple invalidation. + * + * These functions never return error codes. When there is a + * shortage of memory, data is simply not cached. + * When there is a hashing bug, hashing is dropped, and sequential + * searches are used. + */ -#define CACHE_FREE UINT_MAX +/* + * Enter a new hash index, after a new record has been inserted + * + * Do not call when a record has been modified (with no key change) + */ -NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) { - NTFS_CACHE* cache; - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries; - - if(numberOfPages==0 || sectorsPerPage==0) return NULL; - - if (numberOfPages < 4) { - numberOfPages = 4; - } - - if (sectorsPerPage < 32) { - sectorsPerPage = 32; - } - - cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); - if (cache == NULL) { - return NULL; - } - - cache->disc = discInterface; - cache->endOfPartition = endOfPartition; - cache->numberOfPages = numberOfPages; - cache->sectorsPerPage = sectorsPerPage; - - - cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); - if (cacheEntries == NULL) { - ntfs_free (cache); - return NULL; - } - - for (i = 0; i < numberOfPages; i++) { - cacheEntries[i].sector = CACHE_FREE; - cacheEntries[i].count = 0; - cacheEntries[i].last_access = 0; - cacheEntries[i].dirty = false; - cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * BYTES_PER_READ ); - } - - cache->cacheEntries = cacheEntries; - - return cache; -} - -void _NTFS_cache_destructor (NTFS_CACHE* cache) { - unsigned int i; - - if(cache==NULL) return; - - // Clear out cache before destroying it - _NTFS_cache_flush(cache); - - // Free memory in reverse allocation order - for (i = 0; i < cache->numberOfPages; i++) { - ntfs_free (cache->cacheEntries[i].cache); - } - ntfs_free (cache->cacheEntries); - ntfs_free (cache); -} - -static u32 accessCounter = 0; - -static u32 accessTime(){ - accessCounter++; - return accessCounter; -} - -static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +static void inserthashindex(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current) { - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - unsigned int sectorsPerPage = cache->sectorsPerPage; + int h; + struct HASH_ENTRY *link; + struct HASH_ENTRY *first; - bool foundFree = false; - unsigned int oldUsed = 0; - unsigned int oldAccess = UINT_MAX; - - for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { - cacheEntries[i].last_access = accessTime(); - return &(cacheEntries[i]); - } - - if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; - cacheEntries[oldUsed].dirty = false; - } - sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size - sec_t next_page = sector + sectorsPerPage; - if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; - - if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; - - cacheEntries[oldUsed].sector = sector; - cacheEntries[oldUsed].count = next_page-sector; - cacheEntries[oldUsed].last_access = accessTime(); - - return &(cacheEntries[oldUsed]); -} - -static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { - - unsigned int i; - NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; - unsigned int numberOfPages = cache->numberOfPages; - NTFS_CACHE_ENTRY *entry = NULL; - sec_t lowest = UINT_MAX; - - for(i=0;i cacheEntries[i].sector) { - intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + if (cache->dohash) { + h = cache->dohash(current); + if ((h >= 0) && (h < cache->max_hash)) { + /* get a free link and insert at top of hash list */ + link = cache->free_hash; + if (link) { + cache->free_hash = link->next; + first = cache->first_hash[h]; + if (first) + link->next = first; + else + link->next = NULL; + link->entry = current; + cache->first_hash[h] = link; } else { - intersect = cacheEntries[i].sector - sector < count; + ntfs_log_error("No more hash entries," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; } - - if ( intersect && (cacheEntries[i].sector < lowest)) { - lowest = cacheEntries[i].sector; - entry = &cacheEntries[i]; - } - } - } - - return entry; -} - -bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) -{ - sec_t sec; - sec_t secs_to_read; - NTFS_CACHE_ENTRY *entry; - uint8_t *dest = buffer; - - while(numSectors>0) { - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - secs_to_read = entry->count - sec; - if(secs_to_read>numSectors) secs_to_read = numSectors; - - memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ)); - - dest += (secs_to_read*BYTES_PER_READ); - sector += secs_to_read; - numSectors -= secs_to_read; - } - - return true; -} - -/* -Reads some data from a cache page, determined by the sector number -*/ - -bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size); - - return true; -} - -bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { - uint8_t buf[4]; - if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; - - switch(num_bytes) { - case 1: *value = buf[0]; break; - case 2: *value = u8array_to_u16(buf,0); break; - case 4: *value = u8array_to_u32(buf,0); break; - default: return false; - } - return true; -} - -/* -Writes some data to a cache page, making sure it is loaded into memory first. -*/ - -bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size); - - entry->dirty = true; - return true; -} - -bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { - uint8_t buf[4] = {0, 0, 0, 0}; - - switch(size) { - case 1: buf[0] = value; break; - case 2: u16_to_u8array(buf, 0, value); break; - case 4: u32_to_u8array(buf, 0, value); break; - default: return false; - } - - return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); -} - -/* -Writes some data to a cache page, zeroing out the page first -*/ - -bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) -{ - sec_t sec; - NTFS_CACHE_ENTRY *entry; - - if (offset + size > BYTES_PER_READ) return false; - - entry = _NTFS_cache_getPage(cache,sector); - if(entry==NULL) return false; - - sec = sector - entry->sector; - memset(entry->cache + (sec*BYTES_PER_READ),0,BYTES_PER_READ); - memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size); - - entry->dirty = true; - return true; -} - -bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) -{ - sec_t sec; - sec_t secs_to_write; - NTFS_CACHE_ENTRY* entry; - const uint8_t *src = buffer; - - while(numSectors>0) - { - entry = _NTFS_cache_findPage(cache,sector,numSectors); - - if(entry!=NULL) { - - if ( entry->sector > sector) { - - secs_to_write = entry->sector - sector; - - cache->disc->writeSectors(sector,secs_to_write,src); - src += (secs_to_write*BYTES_PER_READ); - sector += secs_to_write; - numSectors -= secs_to_write; - } - - sec = sector - entry->sector; - secs_to_write = entry->count - sec; - - if(secs_to_write>numSectors) secs_to_write = numSectors; - - memcpy(entry->cache + (sec*BYTES_PER_READ),src,(secs_to_write*BYTES_PER_READ)); - - src += (secs_to_write*BYTES_PER_READ); - sector += secs_to_write; - numSectors -= secs_to_write; - - entry->dirty = true; - } else { - cache->disc->writeSectors(sector,numSectors,src); - numSectors=0; + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; } } - return true; } /* -Flushes all dirty pages to disc, clearing the dirty flag. -*/ -bool _NTFS_cache_flush (NTFS_CACHE* cache) { - unsigned int i; - if(cache==NULL) return true; + * Drop a hash index when a record is about to be deleted + */ - for (i = 0; i < cache->numberOfPages; i++) { - if (cache->cacheEntries[i].dirty) { - if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { - return false; +static void drophashindex(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *current, int hash) +{ + struct HASH_ENTRY *link; + struct HASH_ENTRY *previous; + + if (cache->dohash) { + if ((hash >= 0) && (hash < cache->max_hash)) { + /* find the link and unlink */ + link = cache->first_hash[hash]; + previous = (struct HASH_ENTRY*)NULL; + while (link && (link->entry != current)) { + previous = link; + link = link->next; + } + if (link) { + if (previous) + previous->next = link->next; + else + cache->first_hash[hash] = link->next; + link->next = cache->free_hash; + cache->free_hash = link; + } else { + ntfs_log_error("Bad hash list," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } else { + ntfs_log_error("Illegal hash value," + " cache %s hashing dropped\n", + cache->name); + cache->dohash = (cache_hash)NULL; + } + } +} + +/* + * Fetch an entry from cache + * + * returns the cache entry, or NULL if not available + * The returned entry may be modified, but not freed + */ + +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * locate the entry if present + */ + h = cache->dohash(wanted); + link = cache->first_hash[h]; + while (link && compare(link->entry, wanted)) + link = link->next; + if (link) + current = link->entry; + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list if no hash table + * or if hashing has just failed + */ + current = cache->most_recent_entry; + while (current + && compare(current, wanted)) { + current = current->next; + } + } + if (current) { + previous = current->previous; + cache->hits++; + if (previous) { + /* + * found and not at head of list, unlink from current + * position and relink as head of list + */ + previous->next = current->next; + if (current->next) + current->next->previous + = current->previous; + else + cache->oldest_entry + = current->previous; + current->next = cache->most_recent_entry; + current->previous + = (struct CACHED_GENERIC*)NULL; + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; } } - cache->cacheEntries[i].dirty = false; + cache->reads++; } - - return true; + return (current); } -void _NTFS_cache_invalidate (NTFS_CACHE* cache) { - unsigned int i; - if(cache==NULL) - return; +/* + * Enter an inode number into cache + * returns the cache entry or NULL if not possible + */ - _NTFS_cache_flush(cache); - for (i = 0; i < cache->numberOfPages; i++) { - cache->cacheEntries[i].sector = CACHE_FREE; - cache->cacheEntries[i].last_access = 0; - cache->cacheEntries[i].count = 0; - cache->cacheEntries[i].dirty = false; +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *before; + struct HASH_ENTRY *link; + int h; + + current = (struct CACHED_GENERIC*)NULL; + if (cache) { + if (cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link && compare(link->entry, item)) + link = link->next; + if (link) { + current = link->entry; + } + } + if (!cache->dohash) { + /* + * Search sequentially in LRU list to locate the end, + * and find out whether the entry is already in list + * As we normally go to the end, no statistics is + * kept. + */ + current = cache->most_recent_entry; + while (current + && compare(current, item)) { + current = current->next; + } + } + + if (!current) { + /* + * Not in list, get a free entry or reuse the + * last entry, and relink as head of list + * Note : we assume at least three entries, so + * before, previous and first are different when + * an entry is reused. + */ + + if (cache->free_entry) { + current = cache->free_entry; + cache->free_entry = cache->free_entry->next; + if (item->varsize) { + current->variable = ntfs_malloc( + item->varsize); + } else + current->variable = (void*)NULL; + current->varsize = item->varsize; + if (!cache->oldest_entry) + cache->oldest_entry = current; + } else { + /* reusing the oldest entry */ + current = cache->oldest_entry; + before = current->previous; + before->next = (struct CACHED_GENERIC*)NULL; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + if (cache->dofree) + cache->dofree(current); + cache->oldest_entry = current->previous; + if (item->varsize) { + if (current->varsize) + current->variable = realloc( + current->variable, + item->varsize); + else + current->variable = ntfs_malloc( + item->varsize); + } else { + if (current->varsize) + free(current->variable); + current->variable = (void*)NULL; + } + current->varsize = item->varsize; + } + current->next = cache->most_recent_entry; + current->previous = (struct CACHED_GENERIC*)NULL; + if (cache->most_recent_entry) + cache->most_recent_entry->previous = current; + cache->most_recent_entry = current; + memcpy(current->fixed, item->fixed, cache->fixed_size); + if (item->varsize) { + if (current->variable) { + memcpy(current->variable, + item->variable, item->varsize); + } else { + /* + * no more memory for variable part + * recycle entry in free list + * not an error, just uncacheable + */ + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + current = (struct CACHED_GENERIC*)NULL; + } + } else { + current->variable = (void*)NULL; + current->varsize = 0; + } + if (cache->dohash && current) + inserthashindex(cache,current); + } + cache->writes++; + } + return (current); +} + +/* + * Invalidate a cache entry + * The entry is moved to the free entry list + * A specific function may be called for entry deletion + */ + +static void do_invalidate(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *current, int flags) +{ + struct CACHED_GENERIC *previous; + + previous = current->previous; + if ((flags & CACHE_FREE) && cache->dofree) + cache->dofree(current); + /* + * Relink into free list + */ + if (current->next) + current->next->previous = current->previous; + else + cache->oldest_entry = current->previous; + if (previous) + previous->next = current->next; + else + cache->most_recent_entry = current->next; + current->next = cache->free_entry; + cache->free_entry = current; + if (current->variable) + free(current->variable); + current->varsize = 0; + } + + +/* + * Invalidate entries in cache + * + * Several entries may have to be invalidated (at least for inodes + * associated to directories which have been renamed), a different + * compare function may be provided to select entries to invalidate + * + * Returns the number of deleted entries, this can be used by + * the caller to signal a cache corruption if the entry was + * supposed to be found. + */ + +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, cache_compare compare, + int flags) +{ + struct CACHED_GENERIC *current; + struct CACHED_GENERIC *previous; + struct CACHED_GENERIC *next; + struct HASH_ENTRY *link; + int count; + int h; + + current = (struct CACHED_GENERIC*)NULL; + count = 0; + if (cache) { + if (!(flags & CACHE_NOHASH) && cache->dohash) { + /* + * When possible, use the hash table to + * find out whether the entry if present + */ + h = cache->dohash(item); + link = cache->first_hash[h]; + while (link) { + if (compare(link->entry, item)) + link = link->next; + else { + current = link->entry; + link = link->next; + if (current) { + drophashindex(cache,current,h); + do_invalidate(cache, + current,flags); + count++; + } + } + } + } + if ((flags & CACHE_NOHASH) || !cache->dohash) { + /* + * Search sequentially in LRU list + */ + current = cache->most_recent_entry; + previous = (struct CACHED_GENERIC*)NULL; + while (current) { + if (!compare(current, item)) { + next = current->next; + if (cache->dohash) + drophashindex(cache,current, + cache->dohash(current)); + do_invalidate(cache,current,flags); + current = next; + count++; + } else { + previous = current; + current = current->next; + } + } + } + } + return (count); +} + +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags) +{ + int count; + + count = 0; + if (cache) { + if (cache->dohash) + drophashindex(cache,item,cache->dohash(item)); + do_invalidate(cache,item,flags); + count++; + } + return (count); +} + +/* + * Free memory allocated to a cache + */ + +static void ntfs_free_cache(struct CACHE_HEADER *cache) +{ + struct CACHED_GENERIC *entry; + + if (cache) { + for (entry=cache->most_recent_entry; entry; entry=entry->next) { + if (cache->dofree) + cache->dofree(entry); + if (entry->variable) + free(entry->variable); + } + free(cache); } } + +/* + * Create a cache + * + * Returns the cache header, or NULL if the cache could not be created + */ + +static struct CACHE_HEADER *ntfs_create_cache(const char *name, + cache_free dofree, cache_hash dohash, + int full_item_size, + int item_count, int max_hash) +{ + struct CACHE_HEADER *cache; + struct CACHED_GENERIC *pc; + struct CACHED_GENERIC *qc; + struct HASH_ENTRY *ph; + struct HASH_ENTRY *qh; + struct HASH_ENTRY **px; + size_t size; + int i; + + size = sizeof(struct CACHE_HEADER) + item_count*full_item_size; + if (max_hash) + size += item_count*sizeof(struct HASH_ENTRY) + + max_hash*sizeof(struct HASH_ENTRY*); + cache = (struct CACHE_HEADER*)ntfs_malloc(size); + if (cache) { + /* header */ + cache->name = name; + cache->dofree = dofree; + if (dohash && max_hash) { + cache->dohash = dohash; + cache->max_hash = max_hash; + } else { + cache->dohash = (cache_hash)NULL; + cache->max_hash = 0; + } + cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); + cache->reads = 0; + cache->writes = 0; + cache->hits = 0; + /* chain the data entries, and mark an invalid entry */ + cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; + cache->oldest_entry = (struct CACHED_GENERIC*)NULL; + cache->free_entry = &cache->entry[0]; + pc = &cache->entry[0]; + for (i=0; i<(item_count - 1); i++) { + qc = (struct CACHED_GENERIC*)((char*)pc + + full_item_size); + pc->next = qc; + pc->variable = (void*)NULL; + pc->varsize = 0; + pc = qc; + } + /* special for the last entry */ + pc->next = (struct CACHED_GENERIC*)NULL; + pc->variable = (void*)NULL; + pc->varsize = 0; + + if (max_hash) { + /* chain the hash entries */ + ph = (struct HASH_ENTRY*)(((char*)pc) + full_item_size); + cache->free_hash = ph; + for (i=0; i<(item_count - 1); i++) { + qh = &ph[1]; + ph->next = qh; + ph = qh; + } + /* special for the last entry */ + if (item_count) { + ph->next = (struct HASH_ENTRY*)NULL; + } + /* create and initialize the hash indexes */ + px = (struct HASH_ENTRY**)&ph[1]; + cache->first_hash = px; + for (i=0; ifree_hash = (struct HASH_ENTRY*)NULL; + cache->first_hash = (struct HASH_ENTRY**)NULL; + } + } + return (cache); +} + +/* + * Create all LRU caches + * + * No error return, if creation is not possible, cacheing will + * just be not available + */ + +void ntfs_create_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + /* inode cache */ + vol->xinode_cache = ntfs_create_cache("inode",(cache_free)NULL, + ntfs_dir_inode_hash, sizeof(struct CACHED_INODE), + CACHE_INODE_SIZE, 2*CACHE_INODE_SIZE); +#endif +#if CACHE_NIDATA_SIZE + /* idata cache */ + vol->nidata_cache = ntfs_create_cache("nidata", + ntfs_inode_nidata_free, ntfs_inode_nidata_hash, + sizeof(struct CACHED_NIDATA), + CACHE_NIDATA_SIZE, 2*CACHE_NIDATA_SIZE); +#endif +#if CACHE_LOOKUP_SIZE + /* lookup cache */ + vol->lookup_cache = ntfs_create_cache("lookup", + (cache_free)NULL, ntfs_dir_lookup_hash, + sizeof(struct CACHED_LOOKUP), + CACHE_LOOKUP_SIZE, 2*CACHE_LOOKUP_SIZE); +#endif + vol->securid_cache = ntfs_create_cache("securid",(cache_free)NULL, + (cache_hash)NULL,sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE, 0); +#if CACHE_LEGACY_SIZE + vol->legacy_cache = ntfs_create_cache("legacy",(cache_free)NULL, + (cache_hash)NULL, sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE, 0); +#endif +} + +/* + * Free all LRU caches + */ + +void ntfs_free_lru_caches(ntfs_volume *vol) +{ +#if CACHE_INODE_SIZE + ntfs_free_cache(vol->xinode_cache); +#endif +#if CACHE_NIDATA_SIZE + ntfs_free_cache(vol->nidata_cache); +#endif +#if CACHE_LOOKUP_SIZE + ntfs_free_cache(vol->lookup_cache); +#endif + ntfs_free_cache(vol->securid_cache); +#if CACHE_LEGACY_SIZE + ntfs_free_cache(vol->legacy_cache); +#endif +} diff --git a/source/libntfs/cache.h b/source/libntfs/cache.h index d45208bf..67e4f9da 100644 --- a/source/libntfs/cache.h +++ b/source/libntfs/cache.h @@ -1,136 +1,119 @@ /* - NTFS_CACHE.h - The NTFS_CACHE is not visible to the user. It should be flushed - when any file is closed or changes are made to the filesystem. + * cache.h : deal with indexed LRU caches + * + * Copyright (c) 2008-2009 Jean-Pierre Andre + * + * This program/include file 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ - This NTFS_CACHE implements a least-used-page replacement policy. This will - distribute sectors evenly over the pages, so if less than the maximum - pages are used at once, they should all eventually remain in the NTFS_CACHE. - This also has the benefit of throwing out old sectors, so as not to keep - too many stale pages around. +#ifndef _NTFS_CACHE_H_ +#define _NTFS_CACHE_H_ - Copyright (c) 2006 Michael "Chishm" Chisholm - Copyright (c) 2009 shareese, rodries +#include "volume.h" - Redistribution and use in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: +struct CACHED_GENERIC { + struct CACHED_GENERIC *next; + struct CACHED_GENERIC *previous; + void *variable; + size_t varsize; + union { + /* force alignment for pointers and u64 */ + u64 u64align; + void *ptralign; + } fixed[0]; +} ; - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation and/or - other materials provided with the distribution. - 3. The name of the author may not be used to endorse or promote products derived - from this software without specific prior written permission. +struct CACHED_INODE { + struct CACHED_INODE *next; + struct CACHED_INODE *previous; + const char *pathname; + size_t varsize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; +} ; - THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE - LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, - EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ +struct CACHED_NIDATA { + struct CACHED_NIDATA *next; + struct CACHED_NIDATA *previous; + const char *pathname; /* not used */ + size_t varsize; /* not used */ + /* above fields must match "struct CACHED_GENERIC" */ + u64 inum; + ntfs_inode *ni; +} ; -#ifndef _CACHE_H -#define _CACHE_H +struct CACHED_LOOKUP { + struct CACHED_LOOKUP *next; + struct CACHED_LOOKUP *previous; + const char *name; + size_t namesize; + /* above fields must match "struct CACHED_GENERIC" */ + u64 parent; + u64 inum; +} ; -//#include "common.h" -//#include "disc.h" +enum { + CACHE_FREE = 1, + CACHE_NOHASH = 2 +} ; -#include -#include -#include -#include -#include +typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *item); +typedef void (*cache_free)(const struct CACHED_GENERIC *cached); +typedef int (*cache_hash)(const struct CACHED_GENERIC *cached); -#define BYTES_PER_READ 512 +struct HASH_ENTRY { + struct HASH_ENTRY *next; + struct CACHED_GENERIC *entry; +} ; -typedef struct { - sec_t sector; - unsigned int count; - u64 last_access; - bool dirty; - u8* cache; -} NTFS_CACHE_ENTRY; +struct CACHE_HEADER { + const char *name; + struct CACHED_GENERIC *most_recent_entry; + struct CACHED_GENERIC *oldest_entry; + struct CACHED_GENERIC *free_entry; + struct HASH_ENTRY *free_hash; + struct HASH_ENTRY **first_hash; + cache_free dofree; + cache_hash dohash; + unsigned long reads; + unsigned long writes; + unsigned long hits; + int fixed_size; + int max_hash; + struct CACHED_GENERIC entry[0]; +} ; -typedef struct { - const DISC_INTERFACE* disc; - sec_t endOfPartition; - unsigned int numberOfPages; - unsigned int sectorsPerPage; - NTFS_CACHE_ENTRY* cacheEntries; -} NTFS_CACHE; + /* cast to generic, avoiding gcc warnings */ +#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) -/* -Read data from a sector in the NTFS_CACHE -If the sector is not in the NTFS_CACHE, it will be swapped in -offset is the position to start reading from -size is the amount of data to read -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); +struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *wanted, + cache_compare compare); +struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare); +int ntfs_invalidate_cache(struct CACHE_HEADER *cache, + const struct CACHED_GENERIC *item, + cache_compare compare, int flags); +int ntfs_remove_cache(struct CACHE_HEADER *cache, + struct CACHED_GENERIC *item, int flags); -//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); +void ntfs_create_lru_caches(ntfs_volume *vol); +void ntfs_free_lru_caches(ntfs_volume *vol); -/* -Write data to a sector in the NTFS_CACHE -If the sector is not in the NTFS_CACHE, it will be swapped in. -When the sector is swapped out, the data will be written to the disc -offset is the position to start writing to -size is the amount of data to write -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); - -//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); - -/* -Write data to a sector in the NTFS_CACHE, zeroing the sector first -If the sector is not in the NTFS_CACHE, it will be swapped in. -When the sector is swapped out, the data will be written to the disc -offset is the position to start writing to -size is the amount of data to write -Precondition: offset + size <= BYTES_PER_READ -*/ -//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); - -/* -Read several sectors from the NTFS_CACHE -*/ -bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); - -/* -Read a full sector from the NTFS_CACHE -*/ -//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { -// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); -//} - -/* -Write a full sector to the NTFS_CACHE -*/ -//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { -// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); -//} - -bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); - -/* -Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE -*/ -bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); - -/* -Clear out the contents of the NTFS_CACHE without writing any dirty sectors first -*/ -void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); - -NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition); - -void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); - -#endif // _CACHE_H +#endif /* _NTFS_CACHE_H_ */ diff --git a/source/libntfs/cache2.c b/source/libntfs/cache2.c new file mode 100644 index 00000000..872f1a57 --- /dev/null +++ b/source/libntfs/cache2.c @@ -0,0 +1,374 @@ +/* + cache.c + The cache is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This cache implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the cache. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + Copyright (c) 2010 Dimok + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include +#include +#include + +#include "cache2.h" +#include "bit_ops.h" +#include "mem_allocate.h" + +#define CACHE_FREE UINT_MAX + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize) { + NTFS_CACHE* cache; + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries; + + if(numberOfPages==0 || sectorsPerPage==0) return NULL; + + if (numberOfPages < 4) { + numberOfPages = 4; + } + + if (sectorsPerPage < 32) { + sectorsPerPage = 32; + } + + cache = (NTFS_CACHE*) ntfs_alloc (sizeof(NTFS_CACHE)); + if (cache == NULL) { + return NULL; + } + + cache->disc = discInterface; + cache->endOfPartition = endOfPartition; + cache->numberOfPages = numberOfPages; + cache->sectorsPerPage = sectorsPerPage; + cache->sectorSize = sectorSize; + + + cacheEntries = (NTFS_CACHE_ENTRY*) ntfs_alloc ( sizeof(NTFS_CACHE_ENTRY) * numberOfPages); + if (cacheEntries == NULL) { + ntfs_free (cache); + return NULL; + } + + for (i = 0; i < numberOfPages; i++) { + cacheEntries[i].sector = CACHE_FREE; + cacheEntries[i].count = 0; + cacheEntries[i].last_access = 0; + cacheEntries[i].dirty = false; + cacheEntries[i].cache = (uint8_t*) ntfs_align ( sectorsPerPage * cache->sectorSize ); + } + + cache->cacheEntries = cacheEntries; + + return cache; +} + +void _NTFS_cache_destructor (NTFS_CACHE* cache) { + unsigned int i; + + if(cache==NULL) return; + + // Clear out cache before destroying it + _NTFS_cache_flush(cache); + + // Free memory in reverse allocation order + for (i = 0; i < cache->numberOfPages; i++) { + ntfs_free (cache->cacheEntries[i].cache); + } + ntfs_free (cache->cacheEntries); + ntfs_free (cache); +} + +static u32 accessCounter = 0; + +static u32 accessTime(){ + accessCounter++; + return accessCounter; +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_getPage(NTFS_CACHE *cache,sec_t sector) +{ + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + unsigned int sectorsPerPage = cache->sectorsPerPage; + + bool foundFree = false; + unsigned int oldUsed = 0; + unsigned int oldAccess = UINT_MAX; + + for(i=0;i=cacheEntries[i].sector && sector<(cacheEntries[i].sector + cacheEntries[i].count)) { + cacheEntries[i].last_access = accessTime(); + return &(cacheEntries[i]); + } + + if(foundFree==false && (cacheEntries[i].sector==CACHE_FREE || cacheEntries[i].last_accessdisc->writeSectors(cacheEntries[oldUsed].sector,cacheEntries[oldUsed].count,cacheEntries[oldUsed].cache)) return NULL; + cacheEntries[oldUsed].dirty = false; + } + sector = (sector/sectorsPerPage)*sectorsPerPage; // align base sector to page size + sec_t next_page = sector + sectorsPerPage; + if(next_page > cache->endOfPartition) next_page = cache->endOfPartition; + + if(!cache->disc->readSectors(sector,next_page-sector,cacheEntries[oldUsed].cache)) return NULL; + + cacheEntries[oldUsed].sector = sector; + cacheEntries[oldUsed].count = next_page-sector; + cacheEntries[oldUsed].last_access = accessTime(); + + return &(cacheEntries[oldUsed]); +} + +static NTFS_CACHE_ENTRY* _NTFS_cache_findPage(NTFS_CACHE *cache, sec_t sector, sec_t count) { + + unsigned int i; + NTFS_CACHE_ENTRY* cacheEntries = cache->cacheEntries; + unsigned int numberOfPages = cache->numberOfPages; + NTFS_CACHE_ENTRY *entry = NULL; + sec_t lowest = UINT_MAX; + + for(i=0;i cacheEntries[i].sector) { + intersect = sector - cacheEntries[i].sector < cacheEntries[i].count; + } else { + intersect = cacheEntries[i].sector - sector < count; + } + + if ( intersect && (cacheEntries[i].sector < lowest)) { + lowest = cacheEntries[i].sector; + entry = &cacheEntries[i]; + } + } + } + + return entry; +} + +bool _NTFS_cache_readSectors(NTFS_CACHE *cache,sec_t sector,sec_t numSectors,void *buffer) +{ + sec_t sec; + sec_t secs_to_read; + NTFS_CACHE_ENTRY *entry; + uint8_t *dest = buffer; + + while(numSectors>0) { + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + secs_to_read = entry->count - sec; + if(secs_to_read>numSectors) secs_to_read = numSectors; + + memcpy(dest,entry->cache + (sec*cache->sectorSize),(secs_to_read*cache->sectorSize)); + + dest += (secs_to_read*cache->sectorSize); + sector += secs_to_read; + numSectors -= secs_to_read; + } + + return true; +} + +/* +Reads some data from a cache page, determined by the sector number +*/ + +bool _NTFS_cache_readPartialSector (NTFS_CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(buffer,entry->cache + ((sec*cache->sectorSize) + offset),size); + + return true; +} + +bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* cache, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes) { + uint8_t buf[4]; + if (!_NTFS_cache_readPartialSector(cache, buf, sector, offset, num_bytes)) return false; + + switch(num_bytes) { + case 1: *value = buf[0]; break; + case 2: *value = u8array_to_u16(buf,0); break; + case 4: *value = u8array_to_u32(buf,0); break; + default: return false; + } + return true; +} + +/* +Writes some data to a cache page, making sure it is loaded into memory first. +*/ + +bool _NTFS_cache_writePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* cache, const uint32_t value, sec_t sector, unsigned int offset, int size) { + uint8_t buf[4] = {0, 0, 0, 0}; + + switch(size) { + case 1: buf[0] = value; break; + case 2: u16_to_u8array(buf, 0, value); break; + case 4: u32_to_u8array(buf, 0, value); break; + default: return false; + } + + return _NTFS_cache_writePartialSector(cache, buf, sector, offset, size); +} + +/* +Writes some data to a cache page, zeroing out the page first +*/ + +bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +{ + sec_t sec; + NTFS_CACHE_ENTRY *entry; + + if (offset + size > cache->sectorSize) return false; + + entry = _NTFS_cache_getPage(cache,sector); + if(entry==NULL) return false; + + sec = sector - entry->sector; + memset(entry->cache + (sec*cache->sectorSize),0,cache->sectorSize); + memcpy(entry->cache + ((sec*cache->sectorSize) + offset),buffer,size); + + entry->dirty = true; + return true; +} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +{ + sec_t sec; + sec_t secs_to_write; + NTFS_CACHE_ENTRY* entry; + const uint8_t *src = buffer; + + while(numSectors>0) + { + entry = _NTFS_cache_findPage(cache,sector,numSectors); + + if(entry!=NULL) { + + if ( entry->sector > sector) { + + secs_to_write = entry->sector - sector; + + cache->disc->writeSectors(sector,secs_to_write,src); + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + } + + sec = sector - entry->sector; + secs_to_write = entry->count - sec; + + if(secs_to_write>numSectors) secs_to_write = numSectors; + + memcpy(entry->cache + (sec*cache->sectorSize),src,(secs_to_write*cache->sectorSize)); + + src += (secs_to_write*cache->sectorSize); + sector += secs_to_write; + numSectors -= secs_to_write; + + entry->dirty = true; + + } else { + cache->disc->writeSectors(sector,numSectors,src); + numSectors=0; + } + } + return true; +} + +/* +Flushes all dirty pages to disc, clearing the dirty flag. +*/ +bool _NTFS_cache_flush (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) return true; + + for (i = 0; i < cache->numberOfPages; i++) { + if (cache->cacheEntries[i].dirty) { + if (!cache->disc->writeSectors (cache->cacheEntries[i].sector, cache->cacheEntries[i].count, cache->cacheEntries[i].cache)) { + return false; + } + } + cache->cacheEntries[i].dirty = false; + } + + return true; +} + +void _NTFS_cache_invalidate (NTFS_CACHE* cache) { + unsigned int i; + if(cache==NULL) + return; + + _NTFS_cache_flush(cache); + for (i = 0; i < cache->numberOfPages; i++) { + cache->cacheEntries[i].sector = CACHE_FREE; + cache->cacheEntries[i].last_access = 0; + cache->cacheEntries[i].count = 0; + cache->cacheEntries[i].dirty = false; + } +} diff --git a/source/libntfs/cache2.h b/source/libntfs/cache2.h new file mode 100644 index 00000000..21daca7c --- /dev/null +++ b/source/libntfs/cache2.h @@ -0,0 +1,135 @@ +/* + NTFS_CACHE.h + The NTFS_CACHE is not visible to the user. It should be flushed + when any file is closed or changes are made to the filesystem. + + This NTFS_CACHE implements a least-used-page replacement policy. This will + distribute sectors evenly over the pages, so if less than the maximum + pages are used at once, they should all eventually remain in the NTFS_CACHE. + This also has the benefit of throwing out old sectors, so as not to keep + too many stale pages around. + + Copyright (c) 2006 Michael "Chishm" Chisholm + Copyright (c) 2009 shareese, rodries + + Redistribution and use in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + 3. The name of the author may not be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef _CACHE2_H +#define _CACHE2_H + +//#include "common.h" +//#include "disc.h" + +#include +#include +#include +#include +#include + +typedef struct { + sec_t sector; + unsigned int count; + u64 last_access; + bool dirty; + u8* cache; +} NTFS_CACHE_ENTRY; + +typedef struct { + const DISC_INTERFACE* disc; + sec_t endOfPartition; + unsigned int numberOfPages; + unsigned int sectorsPerPage; + sec_t sectorSize; + NTFS_CACHE_ENTRY* cacheEntries; +} NTFS_CACHE; + +/* +Read data from a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in +offset is the position to start reading from +size is the amount of data to read +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_readPartialSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_readLittleEndianValue (NTFS_CACHE* NTFS_CACHE, uint32_t *value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_writePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +//bool _NTFS_cache_writeLittleEndianValue (NTFS_CACHE* NTFS_CACHE, const uint32_t value, sec_t sector, unsigned int offset, int num_bytes); + +/* +Write data to a sector in the NTFS_CACHE, zeroing the sector first +If the sector is not in the NTFS_CACHE, it will be swapped in. +When the sector is swapped out, the data will be written to the disc +offset is the position to start writing to +size is the amount of data to write +Precondition: offset + size <= BYTES_PER_READ +*/ +//bool _NTFS_cache_eraseWritePartialSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector, unsigned int offset, size_t size); + +/* +Read several sectors from the NTFS_CACHE +*/ +bool _NTFS_cache_readSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, void* buffer); + +/* +Read a full sector from the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_readSector (NTFS_CACHE* NTFS_CACHE, void* buffer, sec_t sector) { +// return _NTFS_cache_readPartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +/* +Write a full sector to the NTFS_CACHE +*/ +//static inline bool _NTFS_cache_writeSector (NTFS_CACHE* NTFS_CACHE, const void* buffer, sec_t sector) { +// return _NTFS_cache_writePartialSector (NTFS_CACHE, buffer, sector, 0, BYTES_PER_READ); +//} + +bool _NTFS_cache_writeSectors (NTFS_CACHE* NTFS_CACHE, sec_t sector, sec_t numSectors, const void* buffer); + +/* +Write any dirty sectors back to disc and clear out the contents of the NTFS_CACHE +*/ +bool _NTFS_cache_flush (NTFS_CACHE* NTFS_CACHE); + +/* +Clear out the contents of the NTFS_CACHE without writing any dirty sectors first +*/ +void _NTFS_cache_invalidate (NTFS_CACHE* NTFS_CACHE); + +NTFS_CACHE* _NTFS_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, sec_t sectorSize); + +void _NTFS_cache_destructor (NTFS_CACHE* NTFS_CACHE); + +#endif // _CACHE_H + diff --git a/source/libntfs/collate.c b/source/libntfs/collate.c index 20e5db1e..5f7a015a 100644 --- a/source/libntfs/collate.c +++ b/source/libntfs/collate.c @@ -23,32 +23,23 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif - +#ifdef HAVE_STDLIB_H +#include +#endif #ifdef HAVE_STRING_H #include #endif +#ifdef HAVE_ERRNO_H +#include +#endif +#include "attrib.h" +#include "index.h" #include "collate.h" #include "debug.h" #include "unistr.h" #include "logging.h" -BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr) -{ - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return false - * for everything else. - * JPA added COLLATION_NTOFS_SECURITY_HASH - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG - && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH - && cr != COLLATION_NTOFS_ULONGS) - return FALSE; - return TRUE; -} - /** * ntfs_collate_binary - Which of two binary objects should be listed first * @vol: unused @@ -177,15 +168,15 @@ static int ntfs_collate_ntofs_security_hash(ntfs_volume *vol __attribute__((unus { int rc; u32 d1, d2; - const u32 *p1, *p2; + const le32 *p1, *p2; ntfs_log_trace("Entering.\n"); if (data1_len != data2_len || data1_len != 8) { ntfs_log_error("data1_len or/and data2_len not equal to 8.\n"); return NTFS_COLLATION_ERROR; } - p1 = (const u32*)data1; - p2 = (const u32*)data2; + p1 = (const le32*)data1; + p2 = (const le32*)data2; d1 = le32_to_cpup(p1); d2 = le32_to_cpup(p2); if (d1 < d2) @@ -228,89 +219,53 @@ static int ntfs_collate_file_name(ntfs_volume *vol, const void *data1, const int data1_len __attribute__((unused)), const void *data2, const int data2_len __attribute__((unused))) { + const FILE_NAME_ATTR *file_name_attr1; + const FILE_NAME_ATTR *file_name_attr2; int rc; ntfs_log_trace("Entering.\n"); - rc = ntfs_file_values_compare(data1, data2, NTFS_COLLATION_ERROR, - IGNORE_CASE, vol->upcase, vol->upcase_len); - if (!rc) - rc = ntfs_file_values_compare(data1, data2, - NTFS_COLLATION_ERROR, CASE_SENSITIVE, - vol->upcase, vol->upcase_len); + file_name_attr1 = (const FILE_NAME_ATTR*)data1; + file_name_attr2 = (const FILE_NAME_ATTR*)data2; + rc = ntfs_names_full_collate( + (ntfschar*)&file_name_attr1->file_name, + file_name_attr1->file_name_length, + (ntfschar*)&file_name_attr2->file_name, + file_name_attr2->file_name_length, + CASE_SENSITIVE, vol->upcase, vol->upcase_len); ntfs_log_trace("Done, returning %i.\n", rc); return rc; } -typedef int (*ntfs_collate_func_t)(ntfs_volume *, const void *, const int, - const void *, const int); - -static ntfs_collate_func_t ntfs_do_collate0x0[3] = { - ntfs_collate_binary, - ntfs_collate_file_name, - NULL/*ntfs_collate_unicode_string*/, -}; - -static ntfs_collate_func_t ntfs_do_collate0x1[4] = { - ntfs_collate_ntofs_ulong, - NULL/*ntfs_collate_ntofs_sid*/, - ntfs_collate_ntofs_security_hash, - ntfs_collate_ntofs_ulongs -}; - -/** - * ntfs_collate - collate two data items using a specified collation rule - * @vol: ntfs volume to which the data items belong - * @cr: collation rule to use when comparing the items - * @data1: first data item to collate - * @data1_len: length in bytes of @data1 - * @data2: second data item to collate - * @data2_len: length in bytes of @data2 +/* + * Get a pointer to appropriate collation function. * - * Collate the two data items @data1 and @data2 using the collation rule @cr - * and return -1, 0, or 1 if @data1 is found, respectively, to collate before, - * to match, or to collate after @data2. - * - * For speed we use the collation rule @cr as an index into two tables of - * function pointers to call the appropriate collation function. - * - * Return NTFS_COLLATION_ERROR if error occurred. + * Returns NULL if the needed function is not implemented */ -int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len) -{ - int i; - ntfs_log_trace("Entering.\n"); - if (!vol || !data1 || !data2 || data1_len < 0 || data2_len < 0) { - ntfs_log_error("Invalid arguments passed.\n"); - return NTFS_COLLATION_ERROR; +COLLATE ntfs_get_collate_function(COLLATION_RULES cr) +{ + COLLATE collate; + + switch (cr) { + case COLLATION_BINARY : + collate = ntfs_collate_binary; + break; + case COLLATION_FILE_NAME : + collate = ntfs_collate_file_name; + break; + case COLLATION_NTOFS_SECURITY_HASH : + collate = ntfs_collate_ntofs_security_hash; + break; + case COLLATION_NTOFS_ULONG : + collate = ntfs_collate_ntofs_ulong; + break; + case COLLATION_NTOFS_ULONGS : + collate = ntfs_collate_ntofs_ulongs; + break; + default : + errno = EOPNOTSUPP; + collate = (COLLATE)NULL; + break; } - /* - * FIXME: At the moment we only support COLLATION_BINARY, - * COLLATION_NTOFS_ULONG and COLLATION_FILE_NAME so we return error - * for everything else. - * JPA added COLLATION_NTOFS_SECURITY_HASH - * JPA added COLLATION_NTOFS_ULONGS - */ - if (cr != COLLATION_BINARY && cr != COLLATION_NTOFS_ULONG - && cr != COLLATION_FILE_NAME - && cr != COLLATION_NTOFS_SECURITY_HASH - && cr != COLLATION_NTOFS_ULONGS) - goto err; - i = le32_to_cpu(cr); - if (i < 0) - goto err; - if (i <= 0x02) - return ntfs_do_collate0x0[i](vol, data1, data1_len, - data2, data2_len); - if (i < 0x10) - goto err; - i -= 0x10; - if (i <= 3) - return ntfs_do_collate0x1[i](vol, data1, data1_len, - data2, data2_len); -err: - ntfs_log_debug("Unknown collation rule.\n"); - return NTFS_COLLATION_ERROR; + return (collate); } diff --git a/source/libntfs/collate.h b/source/libntfs/collate.h index 9c0ec9bc..fe383835 100644 --- a/source/libntfs/collate.h +++ b/source/libntfs/collate.h @@ -29,9 +29,6 @@ #define NTFS_COLLATION_ERROR -2 -extern BOOL ntfs_is_collation_rule_supported(COLLATION_RULES cr); -extern int ntfs_collate(ntfs_volume *vol, COLLATION_RULES cr, - const void *data1, const int data1_len, - const void *data2, const int data2_len); +extern COLLATE ntfs_get_collate_function(COLLATION_RULES); #endif /* _NTFS_COLLATE_H */ diff --git a/source/libntfs/compat.c b/source/libntfs/compat.c index e9b3d5df..63114a48 100644 --- a/source/libntfs/compat.c +++ b/source/libntfs/compat.c @@ -77,7 +77,7 @@ int ffs(int x) #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "@(#)daemon.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; #endif /* LIBC_SCCS and not lint */ /* @@ -164,7 +164,7 @@ int daemon(int nochdir, int noclose) { #if defined(LIBC_SCCS) && !defined(lint) static const char sccsid[] = "strsep.c 8.1 (Berkeley) 6/4/93"; -static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.7 2009/03/27 09:09:59 jpandre Exp $"; +static const char rcsid[] = "$Id: compat.c,v 1.1.1.1.2.1 2008-08-16 15:17:44 jpandre Exp $"; #endif /* LIBC_SCCS and not lint */ /* diff --git a/source/libntfs/compress.c b/source/libntfs/compress.c index 4db1826d..fbd30ba9 100644 --- a/source/libntfs/compress.c +++ b/source/libntfs/compress.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Anton Altaparmakov * Copyright (c) 2004-2006 Szabolcs Szakacsits * Copyright (c) 2005 Yura Pakhuchiy - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -118,7 +118,7 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, BOOL done; const unsigned char *key; int c; - unsigned int mxi; + unsigned long mxi; unsigned int mxl; mxl = (1 << (16 - pctx->nbt)) + 2; @@ -147,16 +147,21 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, } } if (!done) { - register unsigned int i; + register unsigned long i; register const unsigned char *p1,*p2; i = 1; - p1 = key; - p2 = &pctx->inbuf[pp]; mxi = NTFS_SB_SIZE - r; - do { - } while ((p1[i] == p2[i]) && (++i < mxi)); - less = (i < mxi) && (p1[i] < p2[i]); + if (mxi < 2) + less = FALSE; + else { + p1 = key; + p2 = &pctx->inbuf[pp]; + /* this loop has a significant impact on performances */ + do { + } while ((p1[i] == p2[i]) && (++i < mxi)); + less = (i < mxi) && (p1[i] < p2[i]); + } if (i >= THRESHOLD) { if (i > pctx->match_length) { pctx->match_position = @@ -174,7 +179,8 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, pctx->rson[i] = r; else pctx->lson[i] = r; - pctx->dad[pp] = NIL; /* remove pp */ + /* remove pp */ + pctx->dad[pp] = NIL; done = TRUE; pctx->match_length = mxl; } @@ -196,7 +202,8 @@ static void ntfs_new_node (struct COMPRESS_CONTEXT *pctx, * or zero if there was a bug */ -static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int rr, int dd) +static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, + unsigned int rr, int dd) { unsigned int bestlen = 0; @@ -214,7 +221,8 @@ static unsigned int ntfs_nextmatch(struct COMPRESS_CONTEXT *pctx, unsigned int r goto bug; } if (((rr + bestlen) < NTFS_SB_SIZE)) { - while ((unsigned int)(1 << pctx->nbt) <= (rr - 1)) + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) pctx->nbt++; ntfs_new_node(pctx,rr); if (pctx->match_length > bestlen) @@ -247,7 +255,8 @@ bug : * or zero if there was an error */ -static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, char *outbuf) +static unsigned int ntfs_compress_block(const char *inbuf, + unsigned int size, char *outbuf) { struct COMPRESS_CONTEXT *pctx; char *ptag; @@ -284,7 +293,8 @@ static unsigned int ntfs_compress_block(const char *inbuf, unsigned int size, ch outbuf[xout++] = inbuf[rr]; ntag++; } else { - while ((unsigned int)(1 << pctx->nbt) <= (rr - 1)) + while ((unsigned int)(1 << pctx->nbt) + <= (rr - 1)) pctx->nbt++; q = (pctx->match_position << (16 - pctx->nbt)) + pctx->match_length - THRESHOLD; @@ -732,7 +742,15 @@ do_next_cb: na->data_size = na->initialized_size = na->allocated_size; do { br = ntfs_attr_pread(na, ofs, to_read, b); - if (br < 0) { + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read an" + " uncompressed cluster," + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)ofs); + errno = EIO; + } err = errno; na->data_size = tdata_size; na->initialized_size = tinitialized_size; @@ -783,7 +801,15 @@ do_next_cb: br = ntfs_attr_pread(na, (vcn << vol->cluster_size_bits) + (cb_pos - cb), to_read, cb_pos); - if (br < 0) { + if (br <= 0) { + if (!br) { + ntfs_log_error("Failed to read a" + " compressed cluster, " + " inode %lld offs 0x%llx\n", + (long long)na->ni->mft_no, + (long long)(vcn << vol->cluster_size_bits)); + errno = EIO; + } err = errno; na->data_size = tdata_size; na->initialized_size = tinitialized_size; @@ -881,11 +907,11 @@ static u32 read_clusters(ntfs_volume *vol, const runlist_element *rl, * Returns the amount of data written */ -static int write_clusters(ntfs_volume *vol, const runlist_element *rl, - s64 offs, int to_write, const char *outbuf) +static s32 write_clusters(ntfs_volume *vol, const runlist_element *rl, + s64 offs, s32 to_write, const char *outbuf) { - int count; - int put, xput; + s32 count; + s32 put, xput; s64 xpos; BOOL first; const char *xoutbuf; @@ -926,17 +952,17 @@ static int write_clusters(ntfs_volume *vol, const runlist_element *rl, * or -2 if there were an irrecoverable error (errno set) */ -static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, - s64 offs, unsigned int insz, const char *inbuf) +static s32 ntfs_comp_set(ntfs_attr *na, runlist_element *rl, + s64 offs, u32 insz, const char *inbuf) { ntfs_volume *vol; char *outbuf; char *pbuf; - unsigned int compsz; - int written; - int rounded; + u32 compsz; + s32 written; + s32 rounded; unsigned int clsz; - unsigned int p; + u32 p; unsigned int sz; unsigned int bsz; BOOL fail; @@ -1002,7 +1028,10 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, rounded = ((compsz - 1) | (clsz - 1)) + 1; written = write_clusters(vol, rl, offs, rounded, outbuf); if (written != rounded) { -// previously written text has been spoilt, should return a specific error + /* + * TODO : previously written text has been + * spoilt, should return a specific error + */ ntfs_log_error("error writing compressed data\n"); errno = EIO; written = -2; @@ -1016,25 +1045,297 @@ static int ntfs_comp_set(ntfs_attr *na, runlist_element *rl, } /* - * Free unneeded clusters after compression + * Check the validity of a compressed runlist + * The check starts at the beginning of current run and ends + * at the end of runlist + * errno is set if the runlist is not valid + */ + +static BOOL valid_compressed_run(ntfs_attr *na, runlist_element *rl, + BOOL fullcheck, const char *text) +{ + runlist_element *xrl; + const char *err; + BOOL ok = TRUE; + + xrl = rl; + while (xrl->vcn & (na->compression_block_clusters - 1)) + xrl--; + err = (const char*)NULL; + while (xrl->length) { + if ((xrl->vcn + xrl->length) != xrl[1].vcn) + err = "Runs not adjacent"; + if (xrl->lcn == LCN_HOLE) { + if ((xrl->vcn + xrl->length) + & (na->compression_block_clusters - 1)) { + err = "Invalid hole"; + } + if (fullcheck && (xrl[1].lcn == LCN_HOLE)) { + err = "Adjacent holes"; + } + } + if (err) { + ntfs_log_error("%s at %s index %ld inode %lld\n", + err, text, (long)(xrl - na->rl), + (long long)na->ni->mft_no); + errno = EIO; + ok = FALSE; + err = (const char*)NULL; + } + xrl++; + } + return (ok); +} + +/* + * Free unneeded clusters after overwriting compressed data * - * This generally requires an empty slot at the end of runlist, + * This generally requires one or two empty slots at the end of runlist, * but we do not want to reallocate the runlist here because * there are many pointers to it. - * So the empty slot has to be reserved beforehand + * So the empty slots have to be reserved beforehand + * + * Returns zero unless some error occurred (described by errno) + * + * +======= start of block =====+ + * 0 |A chunk may overflow | <-- rl usedcnt : A + B + * |A on previous block | then B + * |A | + * +-- end of allocated chunk --+ freelength : C + * |B | (incl overflow) + * +== end of compressed data ==+ + * |C | <-- freerl freecnt : C + D + * |C chunk may overflow | + * |C on next block | + * +-- end of allocated chunk --+ + * |D | + * |D chunk may overflow | + * 15 |D on next block | + * +======== end of block ======+ + * + */ + +static int ntfs_compress_overwr_free(ntfs_attr *na, runlist_element *rl, + s32 usedcnt, s32 freecnt, VCN *update_from) +{ + BOOL beginhole; + BOOL mergeholes; + s32 oldlength; + s32 freelength; + s64 freelcn; + s64 freevcn; + runlist_element *freerl; + ntfs_volume *vol; + s32 carry; + int res; + + vol = na->ni->vol; + res = 0; + freelcn = rl->lcn + usedcnt; + freevcn = rl->vcn + usedcnt; + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + /* can merge with hole before ? */ + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + /* truncate current run, carry to subsequent hole */ + carry = freelength; + oldlength = rl->length; + if (mergeholes) { + /* merging with a hole before */ + freerl = rl; + } else { + rl->length -= freelength; /* warning : can be zero */ + freerl = ++rl; + } + if (!mergeholes && (usedcnt || beginhole)) { + s32 freed; + runlist_element *frl; + runlist_element *erl; + int holes = 0; + BOOL threeparts; + + /* free the unneeded clusters from initial run, then freerl */ + threeparts = (freelength > freecnt); + freed = 0; + frl = freerl; + if (freelength) { + res = ntfs_cluster_free_basic(vol,freelcn, + (threeparts ? freecnt : freelength)); + if (!res) + freed += (threeparts ? freecnt : freelength); + if (!usedcnt) { + holes++; + freerl--; + freerl->length += (threeparts + ? freecnt : freelength); + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + } + } + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + if (!res) { + freed += frl->length; + frl->lcn = LCN_HOLE; + frl->length += carry; + carry = 0; + holes++; + } + } else { + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + if (!res) { + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + frl++; + } + na->compressed_size -= freed << vol->cluster_size_bits; + switch (holes) { + case 0 : + /* there are no hole, must insert one */ + /* space for hole has been prereserved */ + if (freerl->lcn == LCN_HOLE) { + if (threeparts) { + erl = freerl; + while (erl->length) + erl++; + do { + erl[2] = *erl; + } while (erl-- != freerl); + + freerl[1].length = freelength - freecnt; + freerl->length = freecnt; + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[2].lcn = LCN_HOLE; + freerl[2].vcn = freerl[1].vcn + + freerl[1].length; + freerl->vcn = freevcn; + } else { + freerl->vcn = freevcn; + freerl->length += freelength; + } + } else { + erl = freerl; + while (erl->length) + erl++; + if (threeparts) { + do { + erl[2] = *erl; + } while (erl-- != freerl); + freerl[1].lcn = freelcn + freecnt; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = oldlength - usedcnt - freecnt; + } else { + do { + erl[1] = *erl; + } while (erl-- != freerl); + } + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + break; + case 1 : + /* there is a single hole, may have to merge */ + freerl->vcn = freevcn; + if (freerl[1].lcn == LCN_HOLE) { + freerl->length += freerl[1].length; + erl = freerl; + do { + erl++; + *erl = erl[1]; + } while (erl->length); + } + break; + default : + /* there were several holes, must merge them */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + if (freerl[holes].lcn == LCN_HOLE) { + freerl->length += freerl[holes].length; + holes++; + } + erl = freerl; + do { + erl++; + *erl = erl[holes - 1]; + } while (erl->length); + break; + } + } else { + s32 freed; + runlist_element *frl; + runlist_element *xrl; + + freed = 0; + frl = freerl--; + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + while (!res && frl->length && (freed < freecnt)) { + if (frl->length <= (freecnt - freed)) { + freerl->length += frl->length; + freed += frl->length; + res = ntfs_cluster_free_basic(vol, frl->lcn, + frl->length); + frl++; + } else { + freerl->length += freecnt - freed; + res = ntfs_cluster_free_basic(vol, frl->lcn, + freecnt - freed); + frl->lcn += freecnt - freed; + frl->vcn += freecnt - freed; + frl->length -= freecnt - freed; + freed = freecnt; + } + } + /* remove unneded runlist entries */ + xrl = freerl; + /* group with next run if also a hole */ + if (frl->length && (frl->lcn == LCN_HOLE)) { + xrl->length += frl->length; + frl++; + } + while (frl->length) { + *++xrl = *frl++; + } + *++xrl = *frl; /* terminator */ + na->compressed_size -= freed << vol->cluster_size_bits; + } + return (res); +} + + +/* + * Free unneeded clusters after compression + * + * This generally requires one or two empty slots at the end of runlist, + * but we do not want to reallocate the runlist here because + * there are many pointers to it. + * So the empty slots have to be reserved beforehand * * Returns zero unless some error occurred (described by errno) */ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, - s64 used, s64 reserved) + s64 used, s64 reserved, BOOL appending, + VCN *update_from) { - int freecnt; - int usedcnt; + s32 freecnt; + s32 usedcnt; int res; s64 freelcn; s64 freevcn; - int freelength; + s32 freelength; BOOL mergeholes; BOOL beginhole; ntfs_volume *vol; @@ -1044,9 +1345,11 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, vol = na->ni->vol; freecnt = (reserved - used) >> vol->cluster_size_bits; usedcnt = (reserved >> vol->cluster_size_bits) - freecnt; + if (rl->vcn < *update_from) + *update_from = rl->vcn; /* skip entries fully used, if any */ while (rl->length && (rl->length < usedcnt)) { - usedcnt -= rl->length; + usedcnt -= rl->length; /* must be > 0 */ rl++; } if (rl->length) { @@ -1056,62 +1359,93 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, * The required entry has been prereserved when * mapping the runlist. */ + /* get the free part in initial run */ freelcn = rl->lcn + usedcnt; freevcn = rl->vcn + usedcnt; - freelength = rl->length - usedcnt; /* new count of allocated clusters */ - rl->length = usedcnt; /* warning : can be zero */ if (!((freevcn + freecnt) & (na->compression_block_clusters - 1))) { - beginhole = !usedcnt && !rl->vcn; - mergeholes = !usedcnt - && rl[0].vcn - && (rl[-1].lcn == LCN_HOLE); - if (mergeholes) { - freerl = rl; - freerl->length = freecnt; - } else - freerl = ++rl; - if ((freelength > 0) - && !mergeholes - && (usedcnt || beginhole)) { + if (!appending) + res = ntfs_compress_overwr_free(na,rl, + usedcnt,freecnt,update_from); + else { + freelength = rl->length - usedcnt; + beginhole = !usedcnt && !rl->vcn; + mergeholes = !usedcnt + && rl[0].vcn + && (rl[-1].lcn == LCN_HOLE); + if (mergeholes) { + s32 carry; + + /* shorten the runs which have free space */ + carry = freecnt; + freerl = rl; + while (freerl->length < carry) { + carry -= freerl->length; + freerl++; + } + freerl->length = carry; + freerl = rl; + } else { + rl->length = usedcnt; /* can be zero ? */ + freerl = ++rl; + } + if ((freelength > 0) + && !mergeholes + && (usedcnt || beginhole)) { /* * move the unused part to the end. Doing so, * the vcn will be out of order. This does * not harm, the vcn are meaningless now, and * only the lcn are meaningful for freeing. */ - /* locate current end */ - while (rl->length) - rl++; - /* new terminator relocated */ - rl[1].vcn = rl->vcn; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - /* hole, currently allocated */ - rl->vcn = freevcn; - rl->lcn = freelcn; - rl->length = freelength; - } - /* free the hole */ - res = ntfs_cluster_free_from_rl(vol,freerl); - if (!res) { - if (mergeholes) { - /* merge with adjacent hole */ - freerl--; - freerl->length += freecnt; + /* locate current end */ + while (rl->length) + rl++; + /* new terminator relocated */ + rl[1].vcn = rl->vcn; + rl[1].lcn = LCN_ENOENT; + rl[1].length = 0; + /* hole, currently allocated */ + rl->vcn = freevcn; + rl->lcn = freelcn; + rl->length = freelength; } else { - if (beginhole) + /* why is this different from the begin hole case ? */ + if ((freelength > 0) + && !mergeholes + && !usedcnt) { freerl--; - /* mark hole as free */ - freerl->lcn = LCN_HOLE; - freerl->vcn = freevcn; - freerl->length = freecnt; + freerl->length = freelength; + if (freerl->vcn < *update_from) + *update_from + = freerl->vcn; + } + } + /* free the hole */ + res = ntfs_cluster_free_from_rl(vol,freerl); + if (!res) { + na->compressed_size -= freecnt + << vol->cluster_size_bits; + if (mergeholes) { + /* merge with adjacent hole */ + freerl--; + freerl->length += freecnt; + } else { + if (beginhole) + freerl--; + /* mark hole as free */ + freerl->lcn = LCN_HOLE; + freerl->vcn = freevcn; + freerl->length = freecnt; + } + if (freerl->vcn < *update_from) + *update_from = freerl->vcn; + /* and set up the new end */ + freerl[1].lcn = LCN_ENOENT; + freerl[1].vcn = freevcn + freecnt; + freerl[1].length = 0; } - /* and set up the new end */ - freerl[1].lcn = LCN_ENOENT; - freerl[1].vcn = freevcn + freecnt; - freerl[1].length = 0; } } else { ntfs_log_error("Bad end of a compression block set\n"); @@ -1130,7 +1464,7 @@ static int ntfs_compress_free(ntfs_attr *na, runlist_element *rl, */ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, - s64 offs, u32 compsz, int pos, + s64 offs, u32 compsz, s32 pos, BOOL appending, char *outbuf, s64 to_write, const void *b) { int fail = 1; @@ -1147,7 +1481,10 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, compbuf = (char*)ntfs_malloc(compsz); if (compbuf) { /* must align to full block for decompression */ - decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + if (appending) + decompsz = ((pos - 1) | (NTFS_SB_SIZE - 1)) + 1; + else + decompsz = na->compression_block_size; got = read_clusters(na->ni->vol, rl, offs, compsz, compbuf); if ((got == compsz) @@ -1171,7 +1508,8 @@ static int ntfs_read_append(ntfs_attr *na, const runlist_element *rl, */ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, - const char *outbuf, int count, BOOL compress) + const char *outbuf, s32 count, BOOL compress, + BOOL appending, VCN *update_from) { int rounded; int written; @@ -1183,9 +1521,11 @@ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, compress = FALSE; if ((written >= 0) && ntfs_compress_free(na,rl,offs + written, - offs + na->compression_block_size)) + offs + na->compression_block_size, appending, + update_from)) written = -1; - } + } else + written = 0; if (!compress) { clsz = 1 << na->ni->vol->cluster_size_bits; rounded = ((count - 1) | (clsz - 1)) + 1; @@ -1204,32 +1544,55 @@ static int ntfs_flush(ntfs_attr *na, runlist_element *rl, s64 offs, * it has to be reserved beforehand. * * Returns the size of uncompressed data written, - * or zero if an error occurred. + * or negative if an error occurred. * When the returned size is less than requested, new clusters have * to be allocated before the function is called again. */ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part) + const void *b, int compressed_part, + VCN *update_from) { ntfs_volume *vol; runlist_element *brl; /* entry containing the beginning of block */ int compression_length; s64 written; s64 to_read; + s64 to_flush; s64 roffs; s64 got; s64 start_vcn; s64 nextblock; + s64 endwrite; u32 compsz; char *inbuf; char *outbuf; BOOL fail; BOOL done; BOOL compress; + BOOL appending; - written = 0; /* default return */ + if (!valid_compressed_run(na,wrl,FALSE,"begin compressed write")) { + return (-1); + } + if ((*update_from < 0) + || (compressed_part < 0) + || (compressed_part > (int)na->compression_block_clusters)) { + ntfs_log_error("Bad update vcn or compressed_part %d for compressed write\n", + compressed_part); + errno = EIO; + return (-1); + } + /* make sure there are two unused entries in runlist */ + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed write\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; + written = -1; /* default return */ vol = na->ni->vol; compression_length = na->compression_block_clusters; compress = FALSE; @@ -1243,8 +1606,10 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, */ nextblock = ((offs + (wrl->vcn << vol->cluster_size_bits)) | (na->compression_block_size - 1)) + 1; - if ((offs + to_write + (wrl->vcn << vol->cluster_size_bits)) - >= nextblock) { + /* determine whether we are appending to file */ + endwrite = offs + to_write + (wrl->vcn << vol->cluster_size_bits); + appending = endwrite >= na->initialized_size; + if (endwrite >= nextblock) { /* it is time to compress */ compress = TRUE; /* only process what we can */ @@ -1265,6 +1630,8 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, /* find the beginning of block */ start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) & -compression_length; + if (start_vcn < *update_from) + *update_from = start_vcn; while (brl->vcn && (brl->vcn > start_vcn)) { /* jumping back a hole means big trouble */ if (brl->lcn == (LCN)LCN_HOLE) { @@ -1284,14 +1651,24 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, * Decompress the data and append */ compsz = compressed_part << vol->cluster_size_bits; -// improve the needed size outbuf = (char*)ntfs_malloc(na->compression_block_size); if (outbuf) { - to_read = offs - roffs; + if (appending) { + to_read = offs - roffs; + to_flush = to_read + to_write; + } else { + to_read = na->data_size + - (brl->vcn << vol->cluster_size_bits); + if (to_read > na->compression_block_size) + to_read = na->compression_block_size; + to_flush = to_read; + } if (!ntfs_read_append(na, brl, roffs, compsz, - to_read, outbuf, to_write, b)) { + (s32)(offs - roffs), appending, + outbuf, to_write, b)) { written = ntfs_flush(na, brl, roffs, - outbuf, to_read + to_write, compress); + outbuf, to_flush, compress, appending, + update_from); if (written >= 0) { written = to_write; done = TRUE; @@ -1302,9 +1679,9 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, } else { if (compress && !fail) { /* - * we are filling up a block, read the full set of blocks - * and compress it - */ + * we are filling up a block, read the full set + * of blocks and compress it + */ inbuf = (char*)ntfs_malloc(na->compression_block_size); if (inbuf) { to_read = offs - roffs; @@ -1326,7 +1703,8 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, && !ntfs_compress_free(na,brl, written + roffs, na->compression_block_size - + roffs)) { + + roffs, + appending, update_from)) { done = TRUE; written = to_write; } @@ -1354,18 +1732,22 @@ s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *wrl, s64 wpos, } } } + if ((written >= 0) + && !valid_compressed_run(na,wrl,TRUE,"end compressed write")) + written = -1; return (written); } /* * Close a file written compressed. * This compresses the last partial compression block of the file. - * An empty runlist slot has to be reserved beforehand. + * Two empty runlist slots have to be reserved beforehand. * * Returns zero if closing is successful. */ -int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) +int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs, + VCN *update_from) { ntfs_volume *vol; runlist_element *brl; /* entry containing the beginning of block */ @@ -1379,6 +1761,18 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) BOOL fail; BOOL done; + if (na->unused_runs < 2) { + ntfs_log_error("No unused runs for compressed close\n"); + errno = EIO; + return (-1); + } + if (*update_from < 0) { + ntfs_log_error("Bad update vcn for compressed close\n"); + errno = EIO; + return (-1); + } + if (wrl->vcn < *update_from) + *update_from = wrl->vcn; vol = na->ni->vol; compression_length = na->compression_block_clusters; done = FALSE; @@ -1390,7 +1784,10 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) if (inbuf) { start_vcn = (wrl->vcn + (offs >> vol->cluster_size_bits)) & -compression_length; - to_read = offs + ((wrl->vcn - start_vcn) << vol->cluster_size_bits); + if (start_vcn < *update_from) + *update_from = start_vcn; + to_read = offs + ((wrl->vcn - start_vcn) + << vol->cluster_size_bits); brl = wrl; fail = FALSE; while (brl->vcn && (brl->vcn > start_vcn)) { @@ -1403,7 +1800,8 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) } if (!fail) { /* roffs can be an offset from another uncomp block */ - roffs = (start_vcn - brl->vcn) << vol->cluster_size_bits; + roffs = (start_vcn - brl->vcn) + << vol->cluster_size_bits; if (to_read) { got = read_clusters(vol, brl, roffs, to_read, inbuf); @@ -1414,7 +1812,8 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) /* free the unused clusters */ && !ntfs_compress_free(na,brl, written + roffs, - na->compression_block_size + roffs)) { + na->compression_block_size + roffs, + TRUE, update_from)) { done = TRUE; } else /* if compression failed, leave uncompressed */ @@ -1426,5 +1825,7 @@ int ntfs_compressed_close(ntfs_attr *na, runlist_element *wrl, s64 offs) free(inbuf); } } + if (done && !valid_compressed_run(na,wrl,TRUE,"end compressed close")) + done = FALSE; return (!done); } diff --git a/source/libntfs/compress.h b/source/libntfs/compress.h index 809c3c93..c2569321 100644 --- a/source/libntfs/compress.h +++ b/source/libntfs/compress.h @@ -31,9 +31,11 @@ extern s64 ntfs_compressed_attr_pread(ntfs_attr *na, s64 pos, s64 count, extern s64 ntfs_compressed_pwrite(ntfs_attr *na, runlist_element *brl, s64 wpos, s64 offs, s64 to_write, s64 rounded, - const void *b, int compressed_part); + const void *b, int compressed_part, + VCN *update_from); -extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, s64 offs); +extern int ntfs_compressed_close(ntfs_attr *na, runlist_element *brl, + s64 offs, VCN *update_from); #endif /* defined _NTFS_COMPRESS_H */ diff --git a/source/libntfs/config.h b/source/libntfs/config.h index 67109a10..ba0168e1 100644 --- a/source/libntfs/config.h +++ b/source/libntfs/config.h @@ -1,45 +1,43 @@ -/* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ -/* Define if building universal (internal helper macro) */ -/* #undef AC_APPLE_UNIVERSAL_BUILD */ - -//#define DEBUG /* Define to 1 if debug should be enabled */ -#ifdef DEBUG -#define ENABLE_DEBUG -#define NTFS_ENABLE_LOG -#endif +#undef ENABLE_DEBUG + +/* Define to 1 if the nfconv patch should be enabled */ +#undef ENABLE_NFCONV /* Define to 1 if using internal fuse */ -/* #undef FUSE_INTERNAL */ +#undef FUSE_INTERNAL /* Define to 1 if you have the `atexit' function. */ #define HAVE_ATEXIT 1 /* Define to 1 if you have the `basename' function. */ -/* #undef HAVE_BASENAME */ +#undef HAVE_BASENAME /* Define to 1 if you have the header file. */ -/* #undef HAVE_BYTESWAP_H */ +#undef HAVE_BYTESWAP_H + +/* Define to 1 if you have the `clock_gettime' function. */ +#undef HAVE_CLOCK_GETTIME /* Define to 1 if you have the header file. */ #define HAVE_CTYPE_H 1 /* Define to 1 if you have the `daemon' function. */ -/* #undef HAVE_DAEMON */ +#undef HAVE_DAEMON /* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ +#undef HAVE_DLFCN_H /* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ -/* #undef HAVE_DOPRNT */ +#undef HAVE_DOPRNT /* Define to 1 if you have the `dup2' function. */ -/* #undef HAVE_DUP2 */ +#undef HAVE_DUP2 /* Define to 1 if you have the header file. */ -/* #undef HAVE_ENDIAN_H */ +#undef HAVE_ENDIAN_H /* Define to 1 if you have the header file. */ #define HAVE_ERRNO_H 1 @@ -48,10 +46,10 @@ #define HAVE_FCNTL_H 1 /* Define to 1 if you have the `fdatasync' function. */ -/* #undef HAVE_FDATASYNC */ +#undef HAVE_FDATASYNC /* Define to 1 if you have the header file. */ -/* #undef HAVE_FEATURES_H */ +#undef HAVE_FEATURES_H /* Define to 1 if you have the `ffs' function. */ #define HAVE_FFS 1 @@ -60,7 +58,7 @@ #define HAVE_FORK 1 /* Define to 1 if you have the `getmntent' function. */ -/* #undef HAVE_GETMNTENT */ +#undef HAVE_GETMNTENT /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 @@ -68,8 +66,11 @@ /* Define to 1 if you have the `getopt_long' function. */ #define HAVE_GETOPT_LONG 1 +/* Define to 1 if you have the `gettimeofday' function. */ +#undef HAVE_GETTIMEOFDAY + /* Define to 1 if you have the `hasmntopt' function. */ -/* #undef HAVE_HASMNTOPT */ +#undef HAVE_HASMNTOPT /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 @@ -78,19 +79,19 @@ #define HAVE_LIBGEN_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_LIBINTL_H */ +#undef HAVE_LIBINTL_H /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_FD_H */ +#undef HAVE_LINUX_FD_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_HDREG_H */ +#undef HAVE_LINUX_HDREG_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_LINUX_MAJOR_H */ +#undef HAVE_LINUX_MAJOR_H /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 @@ -111,25 +112,25 @@ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_MEMORY_H */ +#undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #define HAVE_MEMSET 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_MNTENT_H */ +#undef HAVE_MNTENT_H /* Define to 1 if you have the `realpath' function. */ -/* #undef HAVE_REALPATH */ +#undef HAVE_REALPATH /* Define to 1 if you have the `regcomp' function. */ -/* #undef HAVE_REGCOMP */ +#undef HAVE_REGCOMP /* Define to 1 if you have the `setlocale' function. */ #define HAVE_SETLOCALE 1 /* Define to 1 if you have the `setxattr' function. */ -/* #undef HAVE_SETXATTR */ +#undef HAVE_SETXATTR /* Define to 1 if `stat' has the bug that it succeeds when given the zero-length file name argument. */ @@ -169,7 +170,7 @@ #define HAVE_STRFTIME 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H */ +#undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 @@ -187,42 +188,44 @@ #define HAVE_STRTOUL 1 /* Define to 1 if `st_atim' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIM */ +#undef HAVE_STRUCT_STAT_ST_ATIM + +/* Define to 1 if `st_atimensec' is member of `struct stat'. */ +#undef HAVE_STRUCT_STAT_ST_ATIMENSEC /* Define to 1 if `st_atimespec' is member of `struct stat'. */ -/* #undef HAVE_STRUCT_STAT_ST_ATIMESPEC */ +#undef HAVE_STRUCT_STAT_ST_ATIMESPEC /* Define to 1 if `st_blocks' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +#undef HAVE_STRUCT_STAT_ST_BLOCKS /* Define to 1 if `st_rdev' is member of `struct stat'. */ -#define HAVE_STRUCT_STAT_ST_RDEV 1 +#undef HAVE_STRUCT_STAT_ST_RDEV /* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ -#define HAVE_ST_BLOCKS 1 -#define HAVE_STRUCT_STAT_ST_BLOCKS 1 +#undef HAVE_ST_BLOCKS /* Define to 1 if you have the `sysconf' function. */ -/* #undef HAVE_SYSCONF */ +#define HAVE_SYSCONF 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYSLOG_H */ +#undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_BYTEORDER_H */ +#undef HAVE_SYS_BYTEORDER_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_ENDIAN_H */ +#undef HAVE_SYS_ENDIAN_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_IOCTL_H */ +#undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MKDEV_H */ +#undef HAVE_SYS_MKDEV_H /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_MOUNT_H */ +#undef HAVE_SYS_MOUNT_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_PARAM_H 1 @@ -234,13 +237,13 @@ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_SYSMACROS_H */ +#undef HAVE_SYS_SYSMACROS_H /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_SYS_VFS_H */ +#undef HAVE_SYS_VFS_H /* Define to 1 if you have the header file. */ #define HAVE_TIME_H 1 @@ -249,13 +252,16 @@ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `utime' function. */ -/* #undef HAVE_UTIME */ +#undef HAVE_UTIME + +/* Define to 1 if you have the `utimensat' function. */ +#undef HAVE_UTIMENSAT /* Define to 1 if you have the header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if `utime(file, NULL)' sets file's timestamp to the present. */ -/* #undef HAVE_UTIME_NULL */ +#undef HAVE_UTIME_NULL /* Define to 1 if you have the `vprintf' function. */ #define HAVE_VPRINTF 1 @@ -264,27 +270,23 @@ #define HAVE_WCHAR_H 1 /* Define to 1 if you have the header file. */ -/* #undef HAVE_WINDOWS_H */ +#undef HAVE_WINDOWS_H /* Define to 1 if the system has the type `_Bool'. */ -#define HAVE__BOOL 1 +#undef HAVE__BOOL /* Don't update /etc/mtab */ -/* #undef IGNORE_MTAB */ +#undef IGNORE_MTAB /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ -/* #undef LSTAT_FOLLOWS_SLASHED_SYMLINK */ - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ +#undef NO_MINUS_C_MINUS_O /* Don't use default IO ops */ -/* #undef NO_NTFS_DEVICE_DEFAULT_IO_OPS */ +#undef NO_NTFS_DEVICE_DEFAULT_IO_OPS /* Name of package */ #define PACKAGE "ntfs-3g" @@ -296,16 +298,16 @@ #define PACKAGE_NAME "ntfs-3g" /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "ntfs-3g 2009.4.4AR.16" +#define PACKAGE_STRING "ntfs-3g 2010.8.8" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "ntfs-3g" /* Define to the version of this package. */ -#define PACKAGE_VERSION "2009.4.4AR.16" +#define PACKAGE_VERSION "2010.8.8" /* POSIX ACL support */ -/* #undef POSIXACLS */ +#undef POSIXACLS /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 @@ -331,59 +333,42 @@ # define __EXTENSIONS__ 1 #endif - /* Version number of package */ -#define VERSION "2009.4.4AR.16" +#define VERSION "2010.8.8" /* Define to 1 if this is a Windows OS */ -/* #undef WINDOWS */ +#undef WINDOWS -/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most - significant byte first (like Motorola and SPARC, unlike Intel). */ -#if defined AC_APPLE_UNIVERSAL_BUILD -# if defined __BIG_ENDIAN__ -# define WORDS_BIGENDIAN 1 -# endif -#else -# ifndef WORDS_BIGENDIAN -# define WORDS_BIGENDIAN 1 -# endif -#endif +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#define WORDS_BIGENDIAN 1 /* Define to 1 if your processor stores words with the least significant byte first (like Intel and VAX, unlike Motorola and SPARC). */ -/* #undef WORDS_LITTLEENDIAN */ +#undef WORDS_LITTLEENDIAN /* Number of bits in a file offset, on hosts where this is settable. */ -/* #undef _FILE_OFFSET_BITS */ +#define _FILE_OFFSET_BITS 64 + +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# undef _GNU_SOURCE +#endif /* Define for large files, on AIX-style hosts. */ -/* #undef _LARGE_FILES */ - -/* Define to 1 if on MINIX. */ -/* #undef _MINIX */ - -/* Define to 2 if the system does not provide POSIX.1 features except with - this defined. */ -/* #undef _POSIX_1_SOURCE */ - -/* Define to 1 if you need to in order for `stat' and other things to work. */ -/* #undef _POSIX_SOURCE */ +#define _LARGE_FILES 1 /* Required define if using POSIX threads */ -/* #undef _REENTRANT */ - -/* Define to empty if `const' does not conform to ANSI C. */ -/* #undef const */ +#undef _REENTRANT /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus -/* #undef inline */ +#define inline __inline__ #endif /* Define to `long int' if does not define. */ -/* #undef off_t */ +#undef off_t /* Define to `unsigned int' if does not define. */ -/* #undef size_t */ +#undef size_t diff --git a/source/libntfs/device_io.h b/source/libntfs/device_io.h index b8701590..fad4d85f 100644 --- a/source/libntfs/device_io.h +++ b/source/libntfs/device_io.h @@ -28,6 +28,18 @@ #ifndef NO_NTFS_DEVICE_DEFAULT_IO_OPS +#ifndef __CYGWIN32__ + +#ifndef GEKKO +/* Not on Cygwin; use standard Unix style low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_unix_io_ops +#else +/* Wii i/o device. */ +#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +#endif + +#else /* __CYGWIN32__ */ + #ifndef HDIO_GETGEO # define HDIO_GETGEO 0x301 /** @@ -53,8 +65,10 @@ struct hd_geometry { # define BLKBSZSET 0x40041271 #endif -/* On Nintendo GameCube/Wii; use Gekko low level device operations. */ -#define ntfs_device_default_io_ops ntfs_device_gekko_io_ops +/* On Cygwin; use Win32 low level device operations. */ +#define ntfs_device_default_io_ops ntfs_device_win32_io_ops + +#endif /* __CYGWIN32__ */ /* Forward declaration. */ diff --git a/source/libntfs/dir.c b/source/libntfs/dir.c index 297f10f1..2372f3f8 100644 --- a/source/libntfs/dir.c +++ b/source/libntfs/dir.c @@ -5,7 +5,7 @@ * Copyright (c) 2004-2005 Richard Russon * Copyright (c) 2004-2008 Szabolcs Szakacsits * Copyright (c) 2005-2007 Yura Pakhuchiy - * Copyright (c) 2008-2009 Jean-Pierre Andre + * Copyright (c) 2008-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -44,6 +44,7 @@ #include #endif +#include "param.h" #include "types.h" #include "debug.h" #include "attrib.h" @@ -55,9 +56,11 @@ #include "ntfstime.h" #include "lcnalloc.h" #include "logging.h" +#include "cache.h" #include "misc.h" #include "security.h" #include "reparse.h" +#include "object_id.h" #ifdef HAVE_SETXATTR #include @@ -85,6 +88,29 @@ ntfschar NTFS_INDEX_R[3] = { const_cpu_to_le16('$'), const_cpu_to_le16('R'), #if CACHE_INODE_SIZE +/* + * Pathname hashing + * + * Based on first char and second char (which may be '\0') + */ + +int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached) +{ + const char *path; + const unsigned char *name; + + path = (const char*)cached->variable; + if (!path) { + ntfs_log_error("Bad inode cache entry\n"); + return (-1); + } + name = (const unsigned char*)strrchr(path,'/'); + if (!name) + name = (const unsigned char*)path; + return (((name[0] << 1) + name[1] + strlen((const char*)name)) + % (2*CACHE_INODE_SIZE)); +} + /* * Pathname comparing for entering/fetching from cache */ @@ -103,21 +129,90 @@ static int inode_cache_compare(const struct CACHED_GENERIC *cached, * related to a renamed directory * inode numbers are also checked, as deleting a long name may * imply deleting a short name and conversely + * + * Only use associated with a CACHE_NOHASH flag */ static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, const struct CACHED_GENERIC *wanted) { int len; + BOOL different; + const struct CACHED_INODE *w; + const struct CACHED_INODE *c; - len = strlen(wanted->variable); - return (!cached->variable - || ((((const struct CACHED_INODE*)wanted)->inum - != MREF(((const struct CACHED_INODE*)cached)->inum)) - && (strncmp((const char*)cached->variable, - (const char*)wanted->variable,len) - || ((((const char*)cached->variable)[len] != '\0') - && (((const char*)cached->variable)[len] != '/'))))); + w = (const struct CACHED_INODE*)wanted; + c = (const struct CACHED_INODE*)cached; + if (w->pathname) { + len = strlen(w->pathname); + different = !cached->variable + || ((w->inum != MREF(c->inum)) + && (strncmp(c->pathname, w->pathname, len) + || ((c->pathname[len] != '\0') + && (c->pathname[len] != '/')))); + } else + different = !c->pathname + || (w->inum != MREF(c->inum)); + return (different); +} + +#endif + +#if CACHE_LOOKUP_SIZE + +/* + * File name comparing for entering/fetching from lookup cache + */ + +static int lookup_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (c->namesize != w->namesize) + || memcmp(c->name, w->name, c->namesize)); +} + +/* + * Inode number comparing for invalidating lookup cache + * + * All entries with designated inode number are invalidated + * + * Only use associated with a CACHE_NOHASH flag + */ + +static int lookup_cache_inv_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + const struct CACHED_LOOKUP *c = (const struct CACHED_LOOKUP*) cached; + const struct CACHED_LOOKUP *w = (const struct CACHED_LOOKUP*) wanted; + return (!c->name + || (c->parent != w->parent) + || (MREF(c->inum) != MREF(w->inum))); +} + +/* + * Lookup hashing + * + * Based on first, second and and last char + */ + +int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached) +{ + const unsigned char *name; + int count; + unsigned int val; + + name = (const unsigned char*)cached->variable; + count = cached->varsize; + if (!name || !count) { + ntfs_log_error("Bad lookup cache entry\n"); + return (-1); + } + val = (name[0] << 2) + (name[1] << 1) + name[count - 1] + count; + return (val % (2*CACHE_LOOKUP_SIZE)); } #endif @@ -147,8 +242,8 @@ static int inode_cache_inv_compare(const struct CACHED_GENERIC *cached, * If the volume is mounted with the case sensitive flag set, then we only * allow exact matches. */ -u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, - const int uname_len) +u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, + const ntfschar *uname, const int uname_len) { VCN vcn; u64 mref = 0; @@ -158,6 +253,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, INDEX_ROOT *ir; INDEX_ENTRY *ie; INDEX_ALLOCATION *ia; + IGNORE_CASE_BOOL case_sensitivity; u8 *index_end; ntfs_attr *ia_na; int eo, rc; @@ -182,6 +278,7 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, "%lld", (unsigned long long)dir_ni->mft_no); goto put_err_out; } + case_sensitivity = (NVolCaseSensitive(vol) ? CASE_SENSITIVE : IGNORE_CASE); /* Get to the index root value. */ ir = (INDEX_ROOT*)((u8*)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); @@ -226,10 +323,10 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_names_collate(uname, uname_len, + rc = ntfs_names_full_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to @@ -238,19 +335,6 @@ u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, if (rc == -1) break; /* The names are not equal, continue the search. */ - if (rc) - continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; if (rc) continue; /* @@ -381,10 +465,10 @@ descend_into_child_node: * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_names_collate(uname, uname_len, + rc = ntfs_names_full_collate(uname, uname_len, (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - IGNORE_CASE, vol->upcase, vol->upcase_len); + ie->key.file_name.file_name_length, + case_sensitivity, vol->upcase, vol->upcase_len); /* * If uname collates before the name of the current entry, there * is definitely no such name in this index but we might need to @@ -395,20 +479,6 @@ descend_into_child_node: /* The names are not equal, continue the search. */ if (rc) continue; - /* - * Names match with case insensitive comparison, now try the - * case sensitive comparison, which is required for proper - * collation. - */ - rc = ntfs_names_collate(uname, uname_len, - (ntfschar*)&ie->key.file_name.file_name, - ie->key.file_name.file_name_length, 1, - CASE_SENSITIVE, vol->upcase, vol->upcase_len); - if (rc == -1) - break; - if (rc) - continue; - mref = le64_to_cpu(ie->indexed_file); free(ia); ntfs_attr_close(ia_na); @@ -463,6 +533,123 @@ close_err_out: goto eo_put_err_out; } +/* + * Lookup a file in a directory from its UTF-8 name + * + * The name is first fetched from cache if one is defined + * + * Returns the inode number + * or -1 if not possible (errno tells why) + */ + +u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name) +{ + int uname_len; + ntfschar *uname = (ntfschar*)NULL; + u64 inum; + char *cached_name; + const char *const_name; + + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + const_name = cached_name; + } else { + cached_name = (char*)NULL; + const_name = name; + } + if (const_name) { +#if CACHE_LOOKUP_SIZE + + /* + * fetch inode from cache + */ + + if (dir_ni->vol->lookup_cache) { + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + + item.name = const_name; + item.namesize = strlen(const_name) + 1; + item.parent = dir_ni->mft_no; + cached = (struct CACHED_LOOKUP*)ntfs_fetch_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) { + inum = cached->inum; + if (inum == (u64)-1) + errno = ENOENT; + } else { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(name, &uname); + if (uname_len >= 0) { + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + item.inum = inum; + /* enter into cache, even if not found */ + ntfs_enter_cache(dir_ni->vol->lookup_cache, + GENERIC(&item), + lookup_cache_compare); + free(uname); + } else + inum = (s64)-1; + } + } else +#endif + { + /* Generate unicode name. */ + uname_len = ntfs_mbstoucs(cached_name, &uname); + if (uname_len >= 0) + inum = ntfs_inode_lookup_by_name(dir_ni, + uname, uname_len); + else + inum = (s64)-1; + } + if (cached_name) + free(cached_name); + } else + inum = (s64)-1; + return (inum); +} + +/* + * Update a cache lookup record when a name has been defined + * + * The UTF-8 name is required + */ + +void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, u64 inum) +{ +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP item; + struct CACHED_LOOKUP *cached; + char *cached_name; + + if (dir_ni->vol->lookup_cache) { + if (!NVolCaseSensitive(dir_ni->vol)) { + cached_name = ntfs_uppercase_mbs(name, + dir_ni->vol->upcase, dir_ni->vol->upcase_len); + item.name = cached_name; + } else { + cached_name = (char*)NULL; + item.name = name; + } + if (item.name) { + item.namesize = strlen(item.name) + 1; + item.parent = dir_ni->mft_no; + item.inum = inum; + cached = (struct CACHED_LOOKUP*)ntfs_enter_cache( + dir_ni->vol->lookup_cache, + GENERIC(&item), lookup_cache_compare); + if (cached) + cached->inum = inum; + if (cached_name) + free(cached_name); + } + } +#endif +} + /** * ntfs_pathname_to_inode - Find the inode which represents the given pathname * @vol: An ntfs volume obtained from ntfs_mount @@ -560,9 +747,46 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, q = strchr(p, PATH_SEP); if (q != NULL) { *q = '\0'; - /* q++; JPA */ } - +#if CACHE_INODE_SIZE + /* + * fetch inode for partial path from cache + */ + cached = (struct CACHED_INODE*)NULL; + if (!parent) { + item.pathname = fullname; + item.varsize = strlen(fullname) + 1; + cached = (struct CACHED_INODE*)ntfs_fetch_cache( + vol->xinode_cache, GENERIC(&item), + inode_cache_compare); + if (cached) { + inum = cached->inum; + } + } + /* + * if not in cache, translate, search, then + * insert into cache if found + */ + if (!cached) { + len = ntfs_mbstoucs(p, &unicode); + if (len < 0) { + ntfs_log_perror("Could not convert filename to Unicode:" + " '%s'", p); + err = errno; + goto close; + } else if (len > NTFS_MAX_NAME_LEN) { + err = ENAMETOOLONG; + goto close; + } + inum = ntfs_inode_lookup_by_name(ni, unicode, len); + if (!parent && (inum != (u64) -1)) { + item.inum = inum; + ntfs_enter_cache(vol->xinode_cache, + GENERIC(&item), + inode_cache_compare); + } + } +#else len = ntfs_mbstoucs(p, &unicode); if (len < 0) { ntfs_log_perror("Could not convert filename to Unicode:" @@ -573,32 +797,6 @@ ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, err = ENAMETOOLONG; goto close; } -#if CACHE_INODE_SIZE - /* - * fetch inode for partial path from cache - * if not available, compute and store into cache - */ - if (parent) - inum = ntfs_inode_lookup_by_name(ni, unicode, len); - else { - item.pathname = fullname; - item.varsize = strlen(fullname) + 1; - cached = (struct CACHED_INODE*)ntfs_fetch_cache( - vol->xinode_cache, GENERIC(&item), - inode_cache_compare); - if (cached) { - inum = cached->inum; - } else { - inum = ntfs_inode_lookup_by_name(ni, unicode, len); - if (inum != (u64) -1) { - item.inum = inum; - ntfs_enter_cache(vol->xinode_cache, - GENERIC(&item), - inode_cache_compare); - } - } - } -#else inum = ntfs_inode_lookup_by_name(ni, unicode, len); #endif if (inum == (u64) -1) { @@ -691,6 +889,10 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, { FILE_NAME_ATTR *fn = &ie->key.file_name; unsigned dt_type; + BOOL metadata; + ntfschar *loname; + int res; + MFT_REF mref; ntfs_log_trace("Entering.\n"); @@ -710,9 +912,38 @@ static int ntfs_filldir(ntfs_inode *dir_ni, s64 *pos, u8 ivcn_bits, dt_type = NTFS_DT_UNKNOWN; else dt_type = NTFS_DT_REG; - return filldir(dirent, fn->file_name, fn->file_name_length, - fn->file_name_type, *pos, - le64_to_cpu(ie->indexed_file), dt_type); + + /* return metadata files and hidden files if requested */ + mref = le64_to_cpu(ie->indexed_file); + metadata = (MREF(mref) != FILE_root) && (MREF(mref) < FILE_first_user); + if ((!metadata && (NVolShowHidFiles(dir_ni->vol) + || !(fn->file_attributes & FILE_ATTR_HIDDEN))) + || (NVolShowSysFiles(dir_ni->vol) && (NVolShowHidFiles(dir_ni->vol) + || metadata))) { + if (NVolCaseSensitive(dir_ni->vol)) { + res = filldir(dirent, fn->file_name, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + } else { + loname = (ntfschar*)ntfs_malloc(2*fn->file_name_length); + if (loname) { + memcpy(loname, fn->file_name, + 2*fn->file_name_length); + ntfs_name_locase(loname, fn->file_name_length, + dir_ni->vol->locase, + dir_ni->vol->upcase_len); + res = filldir(dirent, loname, + fn->file_name_length, + fn->file_name_type, *pos, + mref, dt_type); + free(loname); + } else + res = -1; + } + } else + res = 0; + return (res); } /** @@ -1196,6 +1427,9 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, ni = ntfs_mft_record_alloc(dir_ni->vol, NULL); if (!ni) return NULL; +#if CACHE_NIDATA_SIZE + ntfs_inode_invalidate(dir_ni->vol, ni->mft_no); +#endif /* * Create STANDARD_INFORMATION attribute. * JPA Depending on available inherited security descriptor, @@ -1210,10 +1444,10 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, err = errno; goto err_out; } - si->creation_time = utc2ntfs(ni->creation_time); - si->last_data_change_time = utc2ntfs(ni->last_data_change_time); - si->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - si->last_access_time = utc2ntfs(ni->last_access_time); + si->creation_time = ni->creation_time; + si->last_data_change_time = ni->last_data_change_time; + si->last_mft_change_time = ni->last_mft_change_time; + si->last_access_time = ni->last_access_time; if (securid) { set_nino_flag(ni, v3_Extensions); ni->owner_id = si->owner_id = 0; @@ -1226,7 +1460,21 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, si->file_attributes = FILE_ATTR_SYSTEM; ni->flags = FILE_ATTR_SYSTEM; } + ni->flags |= FILE_ATTR_ARCHIVE; + if (NVolHideDotFiles(dir_ni->vol) + && (name_len > 1) + && (name[0] == const_cpu_to_le16('.')) + && (name[1] != const_cpu_to_le16('.'))) + ni->flags |= FILE_ATTR_HIDDEN; + /* + * Set compression flag according to parent directory + * unless NTFS version < 3.0 or cluster size > 4K + * or compression has been disabled + */ if ((dir_ni->flags & FILE_ATTR_COMPRESSED) + && (dir_ni->vol->major_ver >= 3) + && NVolCompression(dir_ni->vol) + && (dir_ni->vol->cluster_size <= MAX_COMPRESSION_CLUSTER_SIZE) && (S_ISREG(type) || S_ISDIR(type))) ni->flags |= FILE_ATTR_COMPRESSED; /* Add STANDARD_INFORMATION to inode. */ @@ -1355,12 +1603,18 @@ static ntfs_inode *__ntfs_create(ntfs_inode *dir_ni, le32 securid, fn->file_attributes = FILE_ATTR_SYSTEM; else fn->file_attributes |= ni->flags & FILE_ATTR_COMPRESSED; - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->file_attributes |= FILE_ATTR_ARCHIVE; + fn->file_attributes |= ni->flags & FILE_ATTR_HIDDEN; + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + else { + fn->data_size = cpu_to_sle64(ni->data_size); + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + } memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to inode. */ if (ntfs_attr_add(ni, AT_FILE_NAME, AT_UNNAMED, 0, (u8*)fn, fn_len)) { @@ -1521,12 +1775,18 @@ int ntfs_delete(ntfs_volume *vol, const char *pathname, BOOL looking_for_dos_name = FALSE, looking_for_win32_name = FALSE; BOOL case_sensitive_match = TRUE; int err = 0; +#if CACHE_NIDATA_SIZE + int i; +#endif #if CACHE_INODE_SIZE struct CACHED_INODE item; const char *p; u64 inum = (u64)-1; int count; #endif +#if CACHE_LOOKUP_SIZE + struct CACHED_LOOKUP lkitem; +#endif ntfs_log_trace("Entering.\n"); @@ -1640,8 +1900,37 @@ search: * case there are no reference to this inode left, so we should free all * non-resident attributes and mark all MFT record as not in use. */ +#if CACHE_LOOKUP_SIZE + /* invalidate entry in lookup cache */ + lkitem.name = (const char*)NULL; + lkitem.namesize = 0; + lkitem.inum = ni->mft_no; + lkitem.parent = dir_ni->mft_no; + ntfs_invalidate_cache(vol->lookup_cache, GENERIC(&lkitem), + lookup_cache_inv_compare, CACHE_NOHASH); +#endif #if CACHE_INODE_SIZE inum = ni->mft_no; + if (pathname) { + /* invalide cache entry, even if there was an error */ + /* Remove leading /'s. */ + p = pathname; + while (*p == PATH_SEP) + p++; + if (p[0] && (p[strlen(p)-1] == PATH_SEP)) + ntfs_log_error("Unnormalized path %s\n",pathname); + item.pathname = p; + item.varsize = strlen(p); + } else { + item.pathname = (const char*)NULL; + item.varsize = 0; + } + item.inum = inum; + count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), + inode_cache_inv_compare, CACHE_NOHASH); + if (pathname && !count) + ntfs_log_error("Could not delete inode cache entry for %s\n", + pathname); #endif if (ni->mrec->link_count) { ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); @@ -1656,6 +1945,13 @@ search: */ err = errno; } + if (ntfs_delete_object_id_index(ni)) { + /* + * Failed to remove the object id index : proceed anyway + * This is not a critical error. + */ + err = errno; + } ntfs_attr_reinit_search_ctx(actx); while (!ntfs_attrs_walk(actx)) { if (actx->attr->non_resident) { @@ -1684,12 +1980,31 @@ search: "Probably leaving inconsistent metadata.\n"); } /* All extents should be attached after attribute walk. */ +#if CACHE_NIDATA_SIZE + /* + * Disconnect extents before deleting them, so they are + * not wrongly moved to cache through the chainings + */ + for (i=ni->nr_extents-1; i>=0; i--) { + ni->extent_nis[i]->base_ni = (ntfs_inode*)NULL; + ni->extent_nis[i]->nr_extents = 0; + if (ntfs_mft_record_free(ni->vol, ni->extent_nis[i])) { + err = errno; + ntfs_log_error("Failed to free extent MFT record. " + "Leaving inconsistent metadata.\n"); + } + } + free(ni->extent_nis); + ni->nr_extents = 0; + ni->extent_nis = (ntfs_inode**)NULL; +#else while (ni->nr_extents) if (ntfs_mft_record_free(ni->vol, *(ni->extent_nis))) { err = errno; ntfs_log_error("Failed to free extent MFT record. " "Leaving inconsistent metadata.\n"); } +#endif if (ntfs_mft_record_free(ni->vol, ni)) { err = errno; ntfs_log_error("Failed to free base MFT record. " @@ -1705,24 +2020,6 @@ out: err = errno; if (ntfs_inode_close(ni) && !err) err = errno; -#if CACHE_INODE_SIZE - if (pathname) { - /* invalide cache entry, even if there was an error */ - /* Remove leading /'s. */ - p = pathname; - while (*p == PATH_SEP) - p++; - if (p[0] && (p[strlen(p)-1] == PATH_SEP)) - ntfs_log_error("Unnormalized path %s\n",pathname); - item.pathname = p; - item.inum = inum; - count = ntfs_invalidate_cache(vol->xinode_cache, GENERIC(&item), - inode_cache_inv_compare); - if (!count) - ntfs_log_error("Could not delete inode cache entry for %s\n", - pathname); - } -#endif if (err) { errno = err; ntfs_log_debug("Could not delete file: %s\n", strerror(errno)); @@ -1783,14 +2080,17 @@ static int ntfs_link_i(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, fn->file_name_length = name_len; fn->file_name_type = nametype; fn->file_attributes = ni->flags; - if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) { fn->file_attributes |= FILE_ATTR_I30_INDEX_PRESENT; - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + fn->data_size = fn->allocated_size = const_cpu_to_le64(0); + } else { + fn->allocated_size = cpu_to_sle64(ni->allocated_size); + fn->data_size = cpu_to_sle64(ni->data_size); + } + fn->creation_time = ni->creation_time; + fn->last_data_change_time = ni->last_data_change_time; + fn->last_mft_change_time = ni->last_mft_change_time; + fn->last_access_time = ni->last_access_time; memcpy(fn->file_name, name, name_len * sizeof(ntfschar)); /* Add FILE_NAME attribute to index. */ if (ntfs_index_add_filename(dir_ni, fn, MK_MREF(ni->mft_no, @@ -1829,6 +2129,45 @@ int ntfs_link(ntfs_inode *ni, ntfs_inode *dir_ni, ntfschar *name, u8 name_len) return (ntfs_link_i(ni, dir_ni, name, name_len, FILE_NAME_POSIX)); } +/* + * Get a parent directory from an inode entry + * + * This is only used in situations where the path used to access + * the current file is not known for sure. The result may be different + * from the path when the file is linked in several parent directories. + * + * Currently this is only used for translating ".." in the target + * of a Vista relative symbolic link + */ + +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni) +{ + ntfs_inode *dir_ni = (ntfs_inode*)NULL; + u64 inum; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + if (ni->mft_no != FILE_root) { + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return ((ntfs_inode*)NULL); + + if (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + inum = le64_to_cpu(fn->parent_directory); + if (inum != (u64)-1) { + dir_ni = ntfs_inode_open(ni->vol, MREF(inum)); + } + } + ntfs_attr_put_search_ctx(ctx); + } + return (dir_ni); +} + #ifdef HAVE_SETXATTR #define MAX_DOS_NAME_LENGTH 12 @@ -1876,65 +2215,107 @@ static int get_dos_name(ntfs_inode *ni, u64 dnum, ntfschar *dosname) } +/* + * Get a long name for a file in designated directory + * + * Returns size if found + * 0 if not found + * -1 if there was an error (described by errno) + */ + +static int get_long_name(ntfs_inode *ni, u64 dnum, ntfschar *longname) +{ + size_t outsize = 0; + FILE_NAME_ATTR *fn; + ntfs_attr_search_ctx *ctx; + + /* find the name in the attributes */ + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) + return -1; + + /* first search for WIN32 or DOS+WIN32 names */ + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type & FILE_NAME_WIN32) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a WIN32 or WIN32+DOS name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + /* if not found search for POSIX names */ + if (!outsize) { + ntfs_attr_reinit_search_ctx(ctx); + while (!ntfs_attr_lookup(AT_FILE_NAME, AT_UNNAMED, 0, CASE_SENSITIVE, + 0, NULL, 0, ctx)) { + /* We know this will always be resident. */ + fn = (FILE_NAME_ATTR*)((u8*)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + + if ((fn->file_name_type == FILE_NAME_POSIX) + && (MREF_LE(fn->parent_directory) == dnum)) { + /* + * Found a POSIX name for the entry + * copy name + */ + outsize = fn->file_name_length; + memcpy(longname,fn->file_name,outsize*sizeof(ntfschar)); + } + } + } + ntfs_attr_put_search_ctx(ctx); + return (outsize); +} + + /* * Get the ntfs DOS name into an extended attribute */ -int ntfs_get_ntfs_dos_name(const char *path, - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size) { int outsize = 0; char *outname = (char*)NULL; - ntfs_inode *dir_ni = NULL; u64 dnum; - char *dirname; - const char *rdirname; - char *p; int doslen; ntfschar dosname[MAX_DOS_NAME_LENGTH]; - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(ni->vol, NULL, rdirname); - dnum = dir_ni->mft_no; - free(dirname); - } - } - if (dir_ni) { - doslen = get_dos_name(ni, dnum, dosname); - if (doslen > 0) { - /* - * Found a DOS name for the entry, make - * uppercase and encode into the buffer - * if there is enough space - */ - ntfs_name_upcase(dosname, doslen, - ni->vol->upcase, ni->vol->upcase_len); - if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { - ntfs_log_error("Cannot represent dosname in current locale.\n"); - outsize = -errno; - } else { - outsize = strlen(outname); - if (value && (outsize <= (int)size)) - memcpy(value, outname, outsize); - else - if (size && (outsize > (int)size)) - outsize = -ERANGE; - free(outname); - } - } else { - if (doslen == 0) - errno = ENODATA; + dnum = dir_ni->mft_no; + doslen = get_dos_name(ni, dnum, dosname); + if (doslen > 0) { + /* + * Found a DOS name for the entry, make + * uppercase and encode into the buffer + * if there is enough space + */ + ntfs_name_upcase(dosname, doslen, + ni->vol->upcase, ni->vol->upcase_len); + if (ntfs_ucstombs(dosname, doslen, &outname, size) < 0) { + ntfs_log_error("Cannot represent dosname in current locale.\n"); outsize = -errno; + } else { + outsize = strlen(outname); + if (value && (outsize <= (int)size)) + memcpy(value, outname, outsize); + else + if (size && (outsize > (int)size)) + outsize = -ERANGE; + free(outname); } - ntfs_inode_close(dir_ni); - } else + } else { + if (doslen == 0) + errno = ENODATA; outsize = -errno; + } return (outsize); } @@ -2020,7 +2401,6 @@ static int set_namespace(ntfs_inode *ni, ntfs_inode *dir_ni, */ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, - const char *fullpath, ntfschar *shortname, int shortlen, ntfschar *longname, int longlen, ntfschar *deletename, int deletelen, BOOL existed) @@ -2068,17 +2448,22 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, longname, longlen, FILE_NAME_WIN32_AND_DOS) >= 0)) res = 0; - ntfs_inode_close(ni); - ntfs_inode_close(dir_ni); + ntfs_inode_update_times(ni, NTFS_UPDATE_CTIME); + ntfs_inode_update_times(dir_ni, NTFS_UPDATE_MCTIME); + if (ntfs_inode_close_in_dir(ni,dir_ni) && !res) + res = -1; + if (ntfs_inode_close(dir_ni) && !res) + res = -1; } - } else + } else { if (!ntfs_link_i(ni, dir_ni, shortname, shortlen, FILE_NAME_DOS) /* make sure a new link was recorded */ && (le16_to_cpu(ni->mrec->link_count) > linkcount)) { /* delete the existing long name or short name */ - if (!ntfs_delete(vol, fullpath, ni, dir_ni, - deletename, deletelen)) { +// is it ok to not provide the path ? + if (!ntfs_delete(vol, (char*)NULL, ni, dir_ni, + deletename, deletelen)) { /* delete closes the inodes, so have to open again */ dir_ni = ntfs_inode_open(vol, dnum); if (dir_ni) { @@ -2088,15 +2473,20 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, longname, longlen, FILE_NAME_WIN32)) res = 0; - ntfs_inode_close(ni); + if (ntfs_inode_close_in_dir(ni, + dir_ni) + && !res) + res = -1; } - ntfs_inode_close(dir_ni); + if (ntfs_inode_close(dir_ni) && !res) + res = -1; } } } else { - ntfs_inode_close(ni); + ntfs_inode_close_in_dir(ni,dir_ni); ntfs_inode_close(dir_ni); } + } return (res); } @@ -2111,8 +2501,8 @@ static int set_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, * The inode of the file is always closed */ -int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, - int flags, ntfs_inode *ni) +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags) { int res = 0; int longlen = 0; @@ -2125,11 +2515,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, u64 dnum; BOOL closed = FALSE; ntfschar *shortname = NULL; - ntfschar *longname = NULL; - ntfs_inode *dir_ni = NULL; - char *dirname = (char*)NULL; - const char *rdirname; - char *p; + ntfschar longname[NTFS_MAX_NAME_LEN]; vol = ni->vol; fnum = ni->mft_no; @@ -2141,28 +2527,17 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, shortlen = ntfs_mbstoucs(newname, &shortname); /* make sure the short name has valid chars */ if ((shortlen < 0) || ntfs_forbidden_chars(shortname,shortlen)) { - ntfs_inode_close(ni); + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); res = -errno; return res; } - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - longlen = ntfs_mbstoucs(p, &longname); - /* make sure the long name had valid chars */ - if (!ntfs_forbidden_chars(longname,longlen)) { - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(vol, NULL, rdirname); - } - } - } - if (dir_ni) { - dnum = dir_ni->mft_no; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { oldlen = get_dos_name(ni, dnum, oldname); - if (oldlen >= 0) { + if ((oldlen >= 0) + && !ntfs_forbidden_chars(longname, longlen)) { if (oldlen > 0) { if (flags & XATTR_CREATE) { res = -1; @@ -2175,7 +2550,6 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, res = 0; else { res = set_dos_name(ni, dir_ni, - path, shortname, shortlen, longname, longlen, oldname, oldlen, TRUE); @@ -2186,7 +2560,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, res = -1; errno = ENODATA; } else { - res = set_dos_name(ni, dir_ni, path, + res = set_dos_name(ni, dir_ni, shortname, shortlen, longname, longlen, longname, longlen, FALSE); @@ -2195,17 +2569,15 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, } } else res = -1; - if (!closed) - ntfs_inode_close(dir_ni); } else { res = -1; - errno = EINVAL; + errno = ENOENT; } - free(dirname); - free(longname); free(shortname); - if (!closed) - ntfs_inode_close(ni); + if (!closed) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); + } return (res ? -1 : 0); } @@ -2213,7 +2585,7 @@ int ntfs_set_ntfs_dos_name(const char *path, const char *value, size_t size, * Delete the ntfs DOS name */ -int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni) { int res; int oldnametype; @@ -2223,27 +2595,13 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) ntfs_volume *vol; BOOL deleted = FALSE; ntfschar shortname[MAX_DOS_NAME_LENGTH]; - ntfschar *longname = NULL; - ntfs_inode *dir_ni = NULL; - char *dirname = (char*)NULL; - const char *rdirname; - char *p; + ntfschar longname[NTFS_MAX_NAME_LEN]; res = -1; vol = ni->vol; - /* get the parent directory */ - dirname = strdup(path); - if (dirname) { - p = strrchr(dirname,'/'); - if (p) { - *p++ = 0; - longlen = ntfs_mbstoucs(p, &longname); - rdirname = (dirname[0] ? dirname : "/"); - dir_ni = ntfs_pathname_to_inode(vol, NULL, rdirname); - } - } - if (dir_ni) { - dnum = dir_ni->mft_no; + dnum = dir_ni->mft_no; + longlen = get_long_name(ni, dnum, longname); + if (longlen > 0) { shortlen = get_dos_name(ni, dnum, shortname); if (shortlen >= 0) { /* migrate the long name as Posix */ @@ -2278,8 +2636,8 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) */ errno = EIO; ntfs_log_error("Could not change" - " DOS name of %s to Posix\n", - path); + " DOS name of inode %lld to Posix\n", + (long long)ni->mft_no); } break; default : @@ -2288,13 +2646,14 @@ int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni) break; } } - if (!deleted) - ntfs_inode_close(dir_ni); + } else { + errno = ENOENT; + res = -1; + } + if (!deleted) { + ntfs_inode_close_in_dir(ni,dir_ni); + ntfs_inode_close(dir_ni); } - if (!deleted) - ntfs_inode_close(ni); - free(longname); - free(dirname); return (res); } diff --git a/source/libntfs/dir.h b/source/libntfs/dir.h index 49f706fb..56e76fe7 100644 --- a/source/libntfs/dir.h +++ b/source/libntfs/dir.h @@ -61,6 +61,9 @@ extern ntfschar NTFS_INDEX_R[3]; extern u64 ntfs_inode_lookup_by_name(ntfs_inode *dir_ni, const ntfschar *uname, const int uname_len); +extern u64 ntfs_inode_lookup_by_mbsname(ntfs_inode *dir_ni, const char *name); +extern void ntfs_inode_update_mbsname(ntfs_inode *dir_ni, const char *name, + u64 inum); extern ntfs_inode *ntfs_pathname_to_inode(ntfs_volume *vol, ntfs_inode *parent, const char *pathname); @@ -104,12 +107,22 @@ typedef int (*ntfs_filldir_t)(void *dirent, const ntfschar *name, extern int ntfs_readdir(ntfs_inode *dir_ni, s64 *pos, void *dirent, ntfs_filldir_t filldir); -int ntfs_get_ntfs_dos_name(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_dos_name(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); -int ntfs_remove_ntfs_dos_name(const char *path, ntfs_inode *ni); +ntfs_inode *ntfs_dir_parent_inode(ntfs_inode *ni); + +int ntfs_get_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + char *value, size_t size); +int ntfs_set_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni, + const char *value, size_t size, int flags); +int ntfs_remove_ntfs_dos_name(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_INODE_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_dir_inode_hash(const struct CACHED_GENERIC *cached); +extern int ntfs_dir_lookup_hash(const struct CACHED_GENERIC *cached); + +#endif #endif /* defined _NTFS_DIR_H */ diff --git a/source/libntfs/efs.c b/source/libntfs/efs.c index e7a134a2..6ccec20a 100644 --- a/source/libntfs/efs.c +++ b/source/libntfs/efs.c @@ -4,7 +4,7 @@ * This module is part of ntfs-3g library * * Copyright (c) 2009 Martin Bene - * Copyright (c) 2009 Jean-Pierre Andre + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -73,8 +73,7 @@ static ntfschar logged_utility_stream_name[] = { * Get the ntfs EFS info into an extended attribute */ -int ntfs_get_efs_info(const char *path, - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size) { EFS_ATTR_HEADER *efs_info; s64 attr_size = 0; @@ -102,43 +101,133 @@ int ntfs_get_efs_info(const char *path, } else { if (efs_info) { free(efs_info); - ntfs_log_info("Bad efs_info for file %s\n",path); + ntfs_log_error("Bad efs_info for inode %lld\n", + (long long)ni->mft_no); } else { - ntfs_log_info("Could not get efsinfo" - " for file %s\n", path); + ntfs_log_error("Could not get efsinfo" + " for inode %lld\n", + (long long)ni->mft_no); } errno = EIO; attr_size = 0; } } else { errno = ENODATA; - ntfs_log_info("File %s is not encrypted",path); + ntfs_log_trace("Inode %lld is not encrypted\n", + (long long)ni->mft_no); } } return (attr_size ? (int)attr_size : -errno); } +/* + * Fix all encrypted AT_DATA attributes of an inode + * + * The fix may require making an attribute non resident, which + * requires more space in the MFT record, and may cause some + * attribute to be expelled and the full record to be reorganized. + * When this happens, the search for data attributes has to be + * reinitialized. + * + * Returns zero if successful. + * -1 if there is a problem. + */ + +static int fixup_loop(ntfs_inode *ni) +{ + ntfs_attr_search_ctx *ctx; + ntfs_attr *na; + ATTR_RECORD *a; + BOOL restart; + BOOL first; + int cnt; + int maxcnt; + int res = 0; + + maxcnt = 0; + do { + restart = FALSE; + ctx = ntfs_attr_get_search_ctx(ni, NULL); + if (!ctx) { + ntfs_log_error("Failed to get ctx for efs\n"); + res = -1; + } + cnt = 0; + while (!restart && !res + && !ntfs_attr_lookup(AT_DATA, NULL, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + cnt++; + a = ctx->attr; + na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, + (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), + a->name_length); + if (!na) { + ntfs_log_error("can't open DATA Attribute\n"); + res = -1; + } + if (na && !(ctx->attr->flags & ATTR_IS_ENCRYPTED)) { + if (!NAttrNonResident(na) + && ntfs_attr_make_non_resident(na, ctx)) { + /* + * ntfs_attr_make_non_resident fails if there + * is not enough space in the MFT record. + * When this happens, force making non-resident + * so that some other attribute is expelled. + */ + if (ntfs_attr_force_non_resident(na)) { + res = -1; + } else { + /* make sure there is some progress */ + if (cnt <= maxcnt) { + errno = EIO; + ntfs_log_error("Multiple failure" + " making non resident\n"); + res = -1; + } else { + ntfs_attr_put_search_ctx(ctx); + ctx = (ntfs_attr_search_ctx*)NULL; + restart = TRUE; + maxcnt = cnt; + } + } + } + if (!restart && !res + && ntfs_efs_fixup_attribute(ctx, na)) { + ntfs_log_error("Error in efs fixup of AT_DATA Attribute\n"); + res = -1; + } + } + if (na) + ntfs_attr_close(na); + } + first = FALSE; + } while (restart && !res); + if (ctx) + ntfs_attr_put_search_ctx(ctx); + return (res); +} + /* * Set the efs data from an extended attribute * Warning : the new data is not checked * Returns 0, or -1 if there is a problem */ -int ntfs_set_efs_info(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_efs_info(ntfs_inode *ni, const char *value, size_t size, + int flags) + { int res; int written; ntfs_attr *na; const EFS_ATTR_HEADER *info_header; - ntfs_attr_search_ctx *ctx; res = 0; if (ni && value && size) { if (ni->flags & (FILE_ATTR_ENCRYPTED | FILE_ATTR_COMPRESSED)) { if (ni->flags & FILE_ATTR_ENCRYPTED) { - ntfs_log_info("File %s already encrypted",path); + ntfs_log_trace("Inode %lld already encrypted\n", + (long long)ni->mft_no); errno = EEXIST; } else { /* @@ -147,8 +236,8 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), * restored as compressed. * TODO : decompress first. */ - ntfs_log_error("File %s cannot be encrypted and compressed\n", - path); + ntfs_log_error("Inode %lld cannot be encrypted and compressed\n", + (long long)ni->mft_no); errno = EIO; } return -1; @@ -207,20 +296,8 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), /* iterate over AT_DATA attributes */ /* set encrypted flag, truncate attribute to match padding bytes */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) { - ntfs_log_error("Failed to get ctx for efs\n"); - return (-1); - } - while (!ntfs_attr_lookup(AT_DATA, NULL, 0, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_efs_fixup_attribute(ctx, NULL)) { - ntfs_log_error("Error in efs fixup of AT_DATA Attribute"); - ntfs_attr_put_search_ctx(ctx); - return(-1); - } - } - ntfs_attr_put_search_ctx(ctx); + if (fixup_loop(ni)) + return -1; } ni->flags |= FILE_ATTR_ENCRYPTED; NInoSetDirty(ni); @@ -247,15 +324,14 @@ int ntfs_set_efs_info(const char *path __attribute__((unused)), int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) { u64 newsize; + u64 oldsize; le16 appended_bytes; u16 padding_length; - ATTR_RECORD *a; ntfs_inode *ni; - BOOL close_na = FALSE; BOOL close_ctx = FALSE; - if (!ctx && !na) { - ntfs_log_error("neither ctx nor na specified for efs_fixup_attribute\n"); + if (!na) { + ntfs_log_error("no na specified for efs_fixup_attribute\n"); goto err_out; } if (!ctx) { @@ -264,55 +340,79 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) ntfs_log_error("Failed to get ctx for efs\n"); goto err_out; } - close_ctx=TRUE; + close_ctx = TRUE; if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, CASE_SENSITIVE, 0, NULL, 0, ctx)) { ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); goto err_out; } - } - - a = ctx->attr; - if (!na) { - na = ntfs_attr_open(ctx->ntfs_ino, AT_DATA, - (ntfschar*)((u8*)a + le16_to_cpu(a->name_offset)), - a->name_length); - if (!na) { - ntfs_log_error("can't open DATA Attribute\n"); - return (-1); + } else { + if (!NAttrNonResident(na)) { + ntfs_log_error("Cannot make non resident" + " when a context has been allocated\n"); + goto err_out; } - close_na = TRUE; - } - /* make sure size is valid for a raw encrypted stream */ - if ((na->data_size & 511) != 2) { - ntfs_log_error("Bad raw encrypted stream"); - goto err_out; - } - /* read padding length from last two bytes of attribute */ - if (ntfs_attr_pread(na, na->data_size-2, 2, &appended_bytes) != 2) { - ntfs_log_error("Error reading padding length\n"); - goto err_out; - } - padding_length = le16_to_cpu(appended_bytes); - if (padding_length > 511 || padding_length > na->data_size-2) { - errno = EINVAL; - ntfs_log_error("invalid padding length %d for data_size %lld\n", - padding_length, (long long)na->data_size); - goto err_out; - } - newsize = na->data_size - padding_length - 2; - /* truncate attribute to possibly free clusters allocated - for the last two bytes */ - if (ntfs_attr_truncate(na, na->data_size-2)) { - ntfs_log_error("Error truncating attribute\n"); - goto err_out; } - /* Encrypted AT_DATA Attributes MUST be non-resident */ + /* no extra bytes are added to void attributes */ + oldsize = na->data_size; + if (oldsize) { + /* make sure size is valid for a raw encrypted stream */ + if ((oldsize & 511) != 2) { + ntfs_log_error("Bad raw encrypted stream\n"); + goto err_out; + } + /* read padding length from last two bytes of attribute */ + if (ntfs_attr_pread(na, oldsize - 2, 2, &appended_bytes) != 2) { + ntfs_log_error("Error reading padding length\n"); + goto err_out; + } + padding_length = le16_to_cpu(appended_bytes); + if (padding_length > 511 || padding_length > na->data_size-2) { + errno = EINVAL; + ntfs_log_error("invalid padding length %d for data_size %lld\n", + padding_length, (long long)oldsize); + goto err_out; + } + newsize = oldsize - padding_length - 2; + /* + * truncate attribute to possibly free clusters allocated + * for the last two bytes, but do not truncate to new size + * to avoid losing useful data + */ + if (ntfs_attr_truncate(na, oldsize - 2)) { + ntfs_log_error("Error truncating attribute\n"); + goto err_out; + } + } else + newsize = 0; + + /* + * Encrypted AT_DATA Attributes MUST be non-resident + * This has to be done after the attribute is resized, as + * resizing down to zero may cause the attribute to be made + * resident. + */ if (!NAttrNonResident(na) - && ntfs_attr_make_non_resident(na, ctx)) { - ntfs_log_error("Error making DATA attribute non-resident\n"); - goto err_out; + && ntfs_attr_make_non_resident(na, ctx)) { + if (!close_ctx + || ntfs_attr_force_non_resident(na)) { + ntfs_log_error("Error making DATA attribute non-resident\n"); + goto err_out; + } else { + /* + * must reinitialize context after forcing + * non-resident. We need a context for updating + * the state, and at this point, we are sure + * the context is not used elsewhere. + */ + ntfs_attr_reinit_search_ctx(ctx); + if (ntfs_attr_lookup(AT_DATA, na->name, na->name_len, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { + ntfs_log_error("attr lookup for AT_DATA attribute failed in efs fixup\n"); + goto err_out; + } + } } ni = na->ni; if (!na->name_len) { @@ -321,8 +421,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) } NInoSetDirty(ni); NInoFileNameSetDirty(ni); - if (close_na) - ntfs_attr_close(na); ctx->attr->data_size = cpu_to_le64(newsize); if (le64_to_cpu(ctx->attr->initialized_size) > newsize) @@ -333,8 +431,6 @@ int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na) return (0); err_out: - if (close_na && na) - ntfs_attr_close(na); if (close_ctx && ctx) ntfs_attr_put_search_ctx(ctx); return (-1); diff --git a/source/libntfs/efs.h b/source/libntfs/efs.h index bb28e3c7..6eada067 100644 --- a/source/libntfs/efs.h +++ b/source/libntfs/efs.h @@ -21,11 +21,10 @@ #ifndef EFS_H #define EFS_H -int ntfs_get_efs_info(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_efs_info(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); +int ntfs_get_efs_info(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_efs_info(ntfs_inode *ni, + const char *value, size_t size, int flags); int ntfs_efs_fixup_attribute(ntfs_attr_search_ctx *ctx, ntfs_attr *na); #endif /* EFS_H */ diff --git a/source/libntfs/gekko_io.c b/source/libntfs/gekko_io.c index 708f5067..48ca90d4 100644 --- a/source/libntfs/gekko_io.c +++ b/source/libntfs/gekko_io.c @@ -132,22 +132,13 @@ static int ntfs_device_gekko_io_open(struct ntfs_device *dev, int flags) fd->len = (fd->sectorCount * fd->sectorSize); fd->ino = le64_to_cpu(boot.volume_serial_number); - // If the device sector size is not 512 bytes then we cannot continue, - // gekko disc I/O works on the assumption that sectors are always 512 bytes long. - // TODO: Implement support for non-512 byte sector sizes through some fancy maths!? - if (fd->sectorSize != BYTES_PER_SECTOR) { - ntfs_log_error("Boot sector claims there is %i bytes per sector; expected %i\n", fd->sectorSize, BYTES_PER_SECTOR); - errno = EIO; - return -1; - } - // Mark the device as read-only (if required) if (flags & O_RDONLY) { NDevSetReadOnly(dev); } // Create the cache - fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount); + fd->cache = _NTFS_cache_constructor(fd->cachePageCount, fd->cachePageSize, interface, fd->startSector + fd->sectorCount, fd->sectorSize); // Mark the device as open NDevSetBlock(dev); @@ -288,29 +279,31 @@ static s64 ntfs_device_gekko_io_readbytes(struct ntfs_device *dev, s64 offset, s return -1; } + if(offset < 0) + { + errno = EROFS; + return -1; + } + if(!count) return 0; sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = 0; + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this read if (offset > 0) { - sec_start += (sec_t) floor((f64) offset/fd->sectorSize); - buffer_offset = (u32) (offset % fd->sectorSize); + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if (count > fd->sectorSize) { - sec_count = (sec_t) ceil((f64) count/fd->sectorSize); - - if(buffer_offset > 0) - sec_count += 1; + if (buffer_offset+count > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this read happens to be on the sector boundaries then do the read straight into the destination buffer - if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) { + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) { // Read from the device ntfs_log_trace("direct read from sector %d (%d sector(s) long)\n", sec_start, sec_count); @@ -378,29 +371,30 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, return -1; } - if(!count) + if(count < 0 || offset < 0) { + errno = EROFS; + return -1; + } + + if(count == 0) return 0; sec_t sec_start = (sec_t) fd->startSector; sec_t sec_count = 1; - u32 buffer_offset = 0; + u32 buffer_offset = (u32) (offset % fd->sectorSize); u8 *buffer = NULL; // Determine the range of sectors required for this write if (offset > 0) { - sec_start += (sec_t) floor((f64) offset/fd->sectorSize); - buffer_offset = (u32) (offset % fd->sectorSize); + sec_start += (sec_t) floor((f64) offset / (f64) fd->sectorSize); } - if (count > fd->sectorSize) { - sec_count = (sec_t) ceil((f64) count/fd->sectorSize); - - if(buffer_offset > 0) - sec_count += 1; + if ((buffer_offset+count) > fd->sectorSize) { + sec_count = (sec_t) ceil((f64) (buffer_offset+count) / (f64) fd->sectorSize); } // If this write happens to be on the sector boundaries then do the write straight to disc - if((offset % fd->sectorSize == 0) && (count % fd->sectorSize == 0)) { - + if((buffer_offset == 0) && (count % fd->sectorSize == 0)) + { // Write to the device ntfs_log_trace("direct write to sector %d (%d sector(s) long)\n", sec_start, sec_count); if (!ntfs_device_gekko_io_writesectors(dev, sec_start, sec_count, buf)) { @@ -408,10 +402,10 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, errno = EIO; return -1; } - // Else write from a buffer aligned to the sector boundaries - } else { - + } + else + { // Allocate a buffer to hold the write data buffer = (u8 *) ntfs_alloc(sec_count * fd->sectorSize); if (!buffer) { @@ -421,11 +415,23 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, // Read the first and last sectors of the buffer from disc (if required) // NOTE: This is done because the data does not line up with the sector boundaries, // we just read in the buffer edges where the data overlaps with the rest of the disc - if (!ntfs_device_gekko_io_readsectors(dev, sec_start, sec_count, buffer)) { - ntfs_log_perror("read failure @ sector %d\n", sec_start); - ntfs_free(buffer); - errno = EIO; - return -1; + if(buffer_offset != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start, 1, buffer)) { + ntfs_log_perror("read failure @ sector %d\n", sec_start); + ntfs_free(buffer); + errno = EIO; + return -1; + } + } + if((buffer_offset+count) % fd->sectorSize != 0) + { + if (!ntfs_device_gekko_io_readsectors(dev, sec_start + sec_count - 1, 1, buffer + ((sec_count-1) * fd->sectorSize))) { + ntfs_log_perror("read failure @ sector %d\n", sec_start + sec_count - 1); + ntfs_free(buffer); + errno = EIO; + return -1; + } } // Copy the data into the write buffer @@ -445,8 +451,7 @@ static s64 ntfs_device_gekko_io_writebytes(struct ntfs_device *dev, s64 offset, } // Mark the device as dirty (if we actually wrote anything) - if (count > 0) - NDevSetDirty(dev); + NDevSetDirty(dev); return count; } @@ -608,11 +613,6 @@ static int ntfs_device_gekko_io_ioctl(struct ntfs_device *dev, int request, void #if defined(BLKBSZSET) case BLKBSZSET: { int sectorSize = *(int*)argp; - if (sectorSize != BYTES_PER_SECTOR) { - ntfs_log_perror("Attempt to set sector size to an unsupported value (%i), ignored\n", sectorSize); - errno = EOPNOTSUPP; - return -1; - } fd->sectorSize = sectorSize; return 0; } diff --git a/source/libntfs/gekko_io.h b/source/libntfs/gekko_io.h index 5f6ba035..d5018e89 100644 --- a/source/libntfs/gekko_io.h +++ b/source/libntfs/gekko_io.h @@ -26,12 +26,10 @@ #endif #include "types.h" -#include "cache.h" +#include "cache2.h" #include #include -#define BYTES_PER_SECTOR 512 /* Forced by gekko disc i/o */ - /** * gekko_fd - Gekko device driver descriptor */ @@ -44,7 +42,7 @@ typedef struct _gekko_fd { u64 pos; /* Current position within the partition (in bytes) */ u64 len; /* Total length of partition (in bytes) */ ino_t ino; /* Device identifier */ - NTFS_CACHE *cache; /* Cache */ + NTFS_CACHE *cache; /* Cache */ u32 cachePageCount; /* The number of pages in the cache */ u32 cachePageSize; /* The number of sectors per cache page */ } gekko_fd; diff --git a/source/libntfs/index.c b/source/libntfs/index.c index f78336aa..7df0deec 100644 --- a/source/libntfs/index.c +++ b/source/libntfs/index.c @@ -38,9 +38,9 @@ #endif #include "attrib.h" -#include "collate.h" #include "debug.h" #include "index.h" +#include "collate.h" #include "mst.h" #include "dir.h" #include "logging.h" @@ -517,8 +517,13 @@ static int ntfs_ie_lookup(const void *key, const int key_len, * Not a perfect match, need to do full blown collation so we * know which way in the B+tree we have to go. */ - rc = ntfs_collate(icx->ni->vol, icx->cr, key, key_len, &ie->key, - le16_to_cpu(ie->key_length)); + if (!icx->collate) { + ntfs_log_error("Collation function not defined\n"); + errno = EOPNOTSUPP; + return STATUS_ERROR; + } + rc = icx->collate(icx->ni->vol, key, key_len, + &ie->key, le16_to_cpu(ie->key_length)); if (rc == NTFS_COLLATION_ERROR) { ntfs_log_error("Collation error. Perhaps a filename " "contains invalid characters?\n"); @@ -697,12 +702,12 @@ int ntfs_index_lookup(const void *key, const int key_len, ntfs_index_context *ic icx->vcn_size_bits = ni->vol->cluster_size_bits; else icx->vcn_size_bits = ni->vol->sector_size_bits; - - icx->cr = ir->collation_rule; - if (!ntfs_is_collation_rule_supported(icx->cr)) { + /* get the appropriate collation function */ + icx->collate = ntfs_get_collate_function(ir->collation_rule); + if (!icx->collate) { err = errno = EOPNOTSUPP; ntfs_log_perror("Unknown collation rule 0x%x", - (unsigned)le32_to_cpu(icx->cr)); + (unsigned)le32_to_cpu(ir->collation_rule)); goto err_out; } diff --git a/source/libntfs/index.h b/source/libntfs/index.h index 75e7b106..c0e76180 100644 --- a/source/libntfs/index.h +++ b/source/libntfs/index.h @@ -63,6 +63,9 @@ #define MAX_PARENT_VCN 32 +typedef int (*COLLATE)(ntfs_volume *vol, const void *data1, int len1, + const void *data2, int len2); + /** * struct ntfs_index_context - * @ni: inode containing the @entry described by this context @@ -116,7 +119,7 @@ typedef struct { INDEX_ENTRY *entry; void *data; u16 data_len; - COLLATION_RULES cr; + COLLATE collate; BOOL is_in_root; INDEX_ROOT *ir; ntfs_attr_search_ctx *actx; diff --git a/source/libntfs/inode.c b/source/libntfs/inode.c index f6398901..6f3fa060 100644 --- a/source/libntfs/inode.c +++ b/source/libntfs/inode.c @@ -5,6 +5,7 @@ * Copyright (c) 2002-2008 Szabolcs Szakacsits * Copyright (c) 2004-2007 Yura Pakhuchiy * Copyright (c) 2004-2005 Richard Russon + * Copyright (c) 2009-2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -39,10 +40,13 @@ #include #endif +#include "param.h" #include "compat.h" #include "types.h" -#include "attrib.h" +#include "volume.h" +#include "cache.h" #include "inode.h" +#include "attrib.h" #include "debug.h" #include "mft.h" #include "attrlist.h" @@ -120,7 +124,7 @@ ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol) static void __ntfs_inode_release(ntfs_inode *ni) { if (NInoDirty(ni)) - ntfs_log_error("Releasing dirty inode %lld!\n", + ntfs_log_error("Releasing dirty inode %lld!\n", (long long)ni->mft_no); if (NInoAttrList(ni) && ni->attr_list) free(ni->attr_list); @@ -152,7 +156,7 @@ static void __ntfs_inode_release(ntfs_inode *ni) * Return a pointer to the ntfs_inode structure on success or NULL on error, * with errno set to the error code. */ -ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +static ntfs_inode *ntfs_inode_real_open(ntfs_volume *vol, const MFT_REF mref) { s64 l; ntfs_inode *ni = NULL; @@ -190,10 +194,10 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); ni->flags = std_info->file_attributes; - ni->creation_time = ntfs2utc(std_info->creation_time); - ni->last_data_change_time = ntfs2utc(std_info->last_data_change_time); - ni->last_mft_change_time = ntfs2utc(std_info->last_mft_change_time); - ni->last_access_time = ntfs2utc(std_info->last_access_time); + ni->creation_time = std_info->creation_time; + ni->last_data_change_time = std_info->last_data_change_time; + ni->last_mft_change_time = std_info->last_mft_change_time; + ni->last_access_time = std_info->last_access_time; /* JPA insert v3 extensions if present */ /* length may be seen as 72 (v1.x) or 96 (v3.x) */ lthle = ctx->attr->length; @@ -205,13 +209,13 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) ni->usn = std_info->usn; } else { clear_nino_flag(ni, v3_Extensions); - ni->owner_id = 0; - ni->security_id = 0; + ni->owner_id = const_cpu_to_le32(0); + ni->security_id = const_cpu_to_le32(0); } /* Set attribute list information. */ olderrno = errno; - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, 0, 0, NULL, 0, - ctx)) { + if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, AT_UNNAMED, 0, + CASE_SENSITIVE, 0, NULL, 0, ctx)) { if (errno != ENOENT) goto put_err_out; /* Attribute list attribute does not present. */ @@ -239,7 +243,7 @@ ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) if (l != ni->attr_list_size) { errno = EIO; ntfs_log_perror("Unexpected attrlist size (%lld <> %u), inode " - "%lld", (long long)l, ni->attr_list_size, + "%lld", (long long)l, ni->attr_list_size, (long long)MREF(mref)); goto put_err_out; } @@ -266,9 +270,10 @@ get_size: ni->data_size = le32_to_cpu(ctx->attr->value_length); ni->allocated_size = (ni->data_size + 7) & ~7; } + set_nino_flag(ni,KnownSize); } ntfs_attr_put_search_ctx(ctx); -out: +out: ntfs_log_leave("\n"); return ni; @@ -304,10 +309,11 @@ err_out: * EINVAL @ni is invalid (probably it is an extent inode). * EIO I/O error while trying to write inode to disk. */ -int ntfs_inode_close(ntfs_inode *ni) + +int ntfs_inode_real_close(ntfs_inode *ni) { int ret = -1; - + if (!ni) return 0; @@ -324,7 +330,7 @@ int ntfs_inode_close(ntfs_inode *ni) /* Is this a base inode with mapped extent inodes? */ if (ni->nr_extents > 0) { while (ni->nr_extents > 0) { - if (ntfs_inode_close(ni->extent_nis[0])) { + if (ntfs_inode_real_close(ni->extent_nis[0])) { if (errno != EIO) errno = EBUSY; goto err; @@ -364,14 +370,16 @@ int ntfs_inode_close(ntfs_inode *ni) /* Ignore errors, they don't really matter. */ if (tmp_nis) base_ni->extent_nis = tmp_nis; - } else if (tmp_nis) + } else if (tmp_nis) { free(tmp_nis); + base_ni->extent_nis = (ntfs_inode**)NULL; + } /* Allow for error checking. */ i = -1; break; } - - /* + + /* * We could successfully sync, so only log this error * and try to sync other inode extents too. */ @@ -379,7 +387,7 @@ int ntfs_inode_close(ntfs_inode *ni) ntfs_log_error("Extent inode %lld was not found\n", (long long)ni->mft_no); } - + __ntfs_inode_release(ni); ret = 0; err: @@ -387,6 +395,154 @@ err: return ret; } +#if CACHE_NIDATA_SIZE + +/* + * Free an inode structure when there is not more space + * in the cache + */ + +void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached) +{ + ntfs_inode_real_close(((const struct CACHED_NIDATA*)cached)->ni); +} + +/* + * Compute a hash value for an inode entry + */ + +int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item) +{ + return (((const struct CACHED_NIDATA*)item)->inum + % (2*CACHE_NIDATA_SIZE)); +} + +/* + * inum comparing for entering/fetching from cache + */ + +static int idata_cache_compare(const struct CACHED_GENERIC *cached, + const struct CACHED_GENERIC *wanted) +{ + return (((const struct CACHED_NIDATA*)cached)->inum + != ((const struct CACHED_NIDATA*)wanted)->inum); +} + +/* + * Invalidate an inode entry when not needed anymore. + * The entry should have been synced, it may be reused later, + * if it is requested before it is dropped from cache. + */ + +void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref) +{ + struct CACHED_NIDATA item; + int count; + + item.inum = MREF(mref); + item.ni = (ntfs_inode*)NULL; + item.pathname = (const char*)NULL; + item.varsize = 0; + count = ntfs_invalidate_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare,CACHE_FREE); +} + +#endif + +/* + * Open an inode + * + * When possible, an entry recorded in the cache is reused + * + * **NEVER REOPEN** an inode, this can lead to a duplicated + * cache entry (hard to detect), and to an obsolete one being + * reused. System files are however protected from being cached. + */ + +ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref) +{ + ntfs_inode *ni; +#if CACHE_NIDATA_SIZE + struct CACHED_NIDATA item; + struct CACHED_NIDATA *cached; + + /* fetch idata from cache */ + item.inum = MREF(mref); + debug_double_inode(item.inum,1); + item.pathname = (const char*)NULL; + item.varsize = 0; + cached = (struct CACHED_NIDATA*)ntfs_fetch_cache(vol->nidata_cache, + GENERIC(&item),idata_cache_compare); + if (cached) { + ni = cached->ni; + /* do not keep open entries in cache */ + ntfs_remove_cache(vol->nidata_cache, + (struct CACHED_GENERIC*)cached,0); + } else { + ni = ntfs_inode_real_open(vol, mref); + } +#else + ni = ntfs_inode_real_open(vol, mref); +#endif + return (ni); +} + +/* + * Close an inode entry + * + * If cacheing is in use, the entry is synced and kept available + * in cache for further use. + * + * System files (inode < 16 or having the IS_4 flag) are protected + * against being cached. + */ + +int ntfs_inode_close(ntfs_inode *ni) +{ + int res; +#if CACHE_NIDATA_SIZE + BOOL dirty; + struct CACHED_NIDATA item; + + if (ni) { + debug_double_inode(ni->mft_no,0); + /* do not cache system files : could lead to double entries */ + if (ni->vol && ni->vol->nidata_cache + && ((ni->mft_no == FILE_root) + || ((ni->mft_no >= FILE_first_user) + && !(ni->mrec->flags & MFT_RECORD_IS_4)))) { + /* If we have dirty metadata, write it out. */ + dirty = NInoDirty(ni) || NInoAttrListDirty(ni); + if (dirty) { + res = ntfs_inode_sync(ni); + /* do a real close if sync failed */ + if (res) + ntfs_inode_real_close(ni); + } else + res = 0; + + if (!res) { + /* feed idata into cache */ + item.inum = ni->mft_no; + item.ni = ni; + item.pathname = (const char*)NULL; + item.varsize = 0; + debug_cached_inode(ni); + ntfs_enter_cache(ni->vol->nidata_cache, + GENERIC(&item), idata_cache_compare); + } + } else { + /* cache not ready or system file, really close */ + res = ntfs_inode_real_close(ni); + } + } else + res = 0; +#else + res = ntfs_inode_real_close(ni); +#endif + return (res); +} + /** * ntfs_extent_inode_open - load an extent inode and attach it to its base * @base_ni: base ntfs inode @@ -424,11 +580,11 @@ ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref) ntfs_log_perror("%s", __FUNCTION__); return NULL; } - + ntfs_log_enter("Opening extent inode %lld (base mft record %lld).\n", (unsigned long long)mft_no, (unsigned long long)base_ni->mft_no); - + /* Is the extent inode already open and attached to the base inode? */ if (base_ni->nr_extents > 0) { extent_nis = base_ni->extent_nis; @@ -561,11 +717,11 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); std_info->file_attributes = ni->flags; - if (test_nino_flag(ni, TimesDirty)) { - std_info->creation_time = utc2ntfs(ni->creation_time); - std_info->last_data_change_time = utc2ntfs(ni->last_data_change_time); - std_info->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - std_info->last_access_time = utc2ntfs(ni->last_access_time); + if (!test_nino_flag(ni, TimesSet)) { + std_info->creation_time = ni->creation_time; + std_info->last_data_change_time = ni->last_data_change_time; + std_info->last_mft_change_time = ni->last_mft_change_time; + std_info->last_access_time = ni->last_access_time; } /* JPA update v3.x extensions, ensuring consistency */ @@ -595,12 +751,15 @@ static int ntfs_inode_sync_standard_information(ntfs_inode *ni) * * Return 0 on success or -1 on error with errno set to the error code. */ -static int ntfs_inode_sync_file_name(ntfs_inode *ni) +static int ntfs_inode_sync_file_name(ntfs_inode *ni, ntfs_inode *dir_ni) { ntfs_attr_search_ctx *ctx = NULL; ntfs_index_context *ictx; ntfs_inode *index_ni; FILE_NAME_ATTR *fn; + FILE_NAME_ATTR *fnx; + REPARSE_POINT *rpp; + le32 reparse_tag; int err = 0; ntfs_log_trace("Entering for inode %lld\n", (long long)ni->mft_no); @@ -610,6 +769,17 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; goto err_out; } + /* Collect the reparse tag, if any */ + reparse_tag = cpu_to_le32(0); + if (ni->flags & FILE_ATTR_REPARSE_POINT) { + if (!ntfs_attr_lookup(AT_REPARSE_POINT, NULL, + 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { + rpp = (REPARSE_POINT*)((u8 *)ctx->attr + + le16_to_cpu(ctx->attr->value_offset)); + reparse_tag = rpp->reparse_tag; + } + ntfs_attr_reinit_search_ctx(ctx); + } /* Walk through all FILE_NAME attributes and update them. */ while (!ntfs_attr_lookup(AT_FILE_NAME, NULL, 0, 0, 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR *)((u8 *)ctx->attr + @@ -624,7 +794,10 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) */ index_ni = ni; } else - index_ni = ntfs_inode_open(ni->vol, + if (dir_ni) + index_ni = dir_ni; + else + index_ni = ntfs_inode_open(ni->vol, le64_to_cpu(fn->parent_directory)); if (!index_ni) { if (!err) @@ -639,7 +812,8 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) err = errno; ntfs_log_perror("Failed to get index ctx, inode %lld", (long long)index_ni->mft_no); - if (ni != index_ni && ntfs_inode_close(index_ni) && !err) + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) err = errno; continue; } @@ -658,21 +832,34 @@ static int ntfs_inode_sync_file_name(ntfs_inode *ni) continue; } /* Update flags and file size. */ - fn = (FILE_NAME_ATTR *)ictx->data; - fn->file_attributes = - (fn->file_attributes & ~FILE_ATTR_VALID_FLAGS) | + fnx = (FILE_NAME_ATTR *)ictx->data; + fnx->file_attributes = + (fnx->file_attributes & ~FILE_ATTR_VALID_FLAGS) | (ni->flags & FILE_ATTR_VALID_FLAGS); - fn->allocated_size = cpu_to_sle64(ni->allocated_size); - fn->data_size = cpu_to_sle64(ni->data_size); - if (test_nino_flag(ni, TimesDirty)) { - fn->creation_time = utc2ntfs(ni->creation_time); - fn->last_data_change_time = utc2ntfs(ni->last_data_change_time); - fn->last_mft_change_time = utc2ntfs(ni->last_mft_change_time); - fn->last_access_time = utc2ntfs(ni->last_access_time); + if (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + fnx->data_size = fnx->allocated_size + = const_cpu_to_le64(0); + else { + fnx->allocated_size = cpu_to_sle64(ni->allocated_size); + fnx->data_size = cpu_to_sle64(ni->data_size); + } + /* update or clear the reparse tag in the index */ + fnx->reparse_point_tag = reparse_tag; + if (!test_nino_flag(ni, TimesSet)) { + fnx->creation_time = ni->creation_time; + fnx->last_data_change_time = ni->last_data_change_time; + fnx->last_mft_change_time = ni->last_mft_change_time; + fnx->last_access_time = ni->last_access_time; + } else { + fnx->creation_time = fn->creation_time; + fnx->last_data_change_time = fn->last_data_change_time; + fnx->last_mft_change_time = fn->last_mft_change_time; + fnx->last_access_time = fn->last_access_time; } ntfs_index_entry_mark_dirty(ictx); ntfs_index_ctx_put(ictx); - if ((ni != index_ni) && ntfs_inode_close(index_ni) && !err) + if ((ni != index_ni) && !dir_ni + && ntfs_inode_close(index_ni) && !err) err = errno; } /* Check for real error occurred. */ @@ -714,11 +901,10 @@ err_out: * EBUSY - Inode and/or one of its extents is busy, try again later. * EIO - I/O error while writing the inode (or one of its extents). */ -int ntfs_inode_sync(ntfs_inode *ni) +static int ntfs_inode_sync_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) { int ret = 0; int err = 0; - if (!ni) { errno = EINVAL; ntfs_log_error("Failed to sync NULL inode\n"); @@ -740,7 +926,7 @@ int ntfs_inode_sync(ntfs_inode *ni) /* Update FILE_NAME's in the index. */ if ((ni->mrec->flags & MFT_RECORD_IN_USE) && ni->nr_extents != -1 && NInoFileNameTestAndClearDirty(ni) && - ntfs_inode_sync_file_name(ni)) { + ntfs_inode_sync_file_name(ni, dir_ni)) { if (!err || errno == EIO) { err = errno; if (err != EIO) @@ -768,8 +954,8 @@ int ntfs_inode_sync(ntfs_inode *ni) } NInoAttrListSetDirty(ni); goto sync_inode; - } - + } + if (na->data_size == ni->attr_list_size) { if (ntfs_attr_pwrite(na, 0, ni->attr_list_size, ni->attr_list) != ni->attr_list_size) { @@ -791,7 +977,7 @@ int ntfs_inode_sync(ntfs_inode *ni) } ntfs_attr_close(na); } - + sync_inode: /* Write this inode out to the $MFT (and $MFTMirr if applicable). */ if (NInoTestAndClearDirty(ni)) { @@ -817,8 +1003,8 @@ sync_inode: eni = ni->extent_nis[i]; if (!NInoTestAndClearDirty(eni)) continue; - - if (ntfs_mft_record_write(eni->vol, eni->mft_no, + + if (ntfs_mft_record_write(eni->vol, eni->mft_no, eni->mrec)) { if (!err || errno == EIO) { err = errno; @@ -838,11 +1024,33 @@ sync_inode: errno = err; ret = -1; } - + ntfs_log_leave("\n"); return ret; } +int ntfs_inode_sync(ntfs_inode *ni) +{ + return (ntfs_inode_sync_in_dir(ni, (ntfs_inode*)NULL)); +} + +/* + * Close an inode with an open parent inode + */ + +int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni) +{ + int res; + + res = ntfs_inode_sync_in_dir(ni, dir_ni); + if (res) { + if (errno != EIO) + errno = EBUSY; + } else + res = ntfs_inode_close(ni); + return (res); +} + /** * ntfs_inode_add_attrlist - add attribute list to inode and fill it * @ni: opened ntfs inode to which add attribute list @@ -885,19 +1093,19 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) } /* Walk through all attributes. */ while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - + int ale_size; - + if (ctx->attr->type == AT_ATTRIBUTE_LIST) { err = EIO; ntfs_log_perror("Attribute list already present"); goto put_err_out; } - + ale_size = (sizeof(ATTR_LIST_ENTRY) + sizeof(ntfschar) * ctx->attr->name_length + 7) & ~7; al_len += ale_size; - + aln = realloc(al, al_len); if (!aln) { err = errno; @@ -906,9 +1114,9 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) } ale = (ATTR_LIST_ENTRY *)(aln + ((u8 *)ale - al)); al = aln; - + memset(ale, 0, ale_size); - + /* Add attribute to attribute list. */ ale->type = ctx->attr->type; ale->length = cpu_to_le16((sizeof(ATTR_LIST_ENTRY) + @@ -975,7 +1183,7 @@ int ntfs_inode_add_attrlist(ntfs_inode *ni) ntfs_attr_close(na); goto remove_attrlist_record;; } - + ntfs_attr_put_search_ctx(ctx); ntfs_attr_close(na); return 0; @@ -1076,12 +1284,12 @@ int ntfs_inode_free_space(ntfs_inode *ni, int size) * find next, because we don't need such. */ while (ctx->ntfs_ino->mft_no != ni->mft_no) { -retry: +retry: if (ntfs_attr_position(AT_UNUSED, ctx)) goto put_err_out; } - if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && + if (ntfs_inode_base(ctx->ntfs_ino)->mft_no == FILE_MFT && ctx->attr->type == AT_DATA) goto retry; @@ -1102,10 +1310,10 @@ retry: return 0; } /* - * Reposition to first attribute after $STANDARD_INFORMATION - * and $ATTRIBUTE_LIST instead of simply skipping this attribute - * because in the case when we have got only in-memory attribute - * list then ntfs_attr_lookup will fail when it tries to find + * Reposition to first attribute after $STANDARD_INFORMATION + * and $ATTRIBUTE_LIST instead of simply skipping this attribute + * because in the case when we have got only in-memory attribute + * list then ntfs_attr_lookup will fail when it tries to find * $ATTRIBUTE_LIST. */ ntfs_attr_reinit_search_ctx(ctx); @@ -1129,7 +1337,7 @@ put_err_out: */ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) { - time_t now; + ntfs_time now; if (!ni) { ntfs_log_error("%s(): Invalid arguments.\n", __FUNCTION__); @@ -1140,15 +1348,14 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) NVolReadOnly(ni->vol) || !mask) return; - now = time(NULL); + now = ntfs_current_time(); if (mask & NTFS_UPDATE_ATIME) ni->last_access_time = now; if (mask & NTFS_UPDATE_MTIME) ni->last_data_change_time = now; if (mask & NTFS_UPDATE_CTIME) ni->last_mft_change_time = now; - - set_nino_flag(ni, TimesDirty); + NInoFileNameSetDirty(ni); NInoSetDirty(ni); } @@ -1161,7 +1368,7 @@ void ntfs_inode_update_times(ntfs_inode *ni, ntfs_time_update_flags mask) * Check if the mft record given by @mft_no and @attr contains the bad sector * list. Please note that mft record numbers describing $Badclus extent inodes * will not match the current $Badclus:$Bad check. - * + * * On success return 1 if the file is $Badclus:$Bad, otherwise return 0. * On error return -1 with errno set to the error code. */ @@ -1175,7 +1382,7 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) errno = EINVAL; return -1; } - + if (mft_no != FILE_BadClus) return 0; @@ -1209,8 +1416,7 @@ int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *attr) * -errno if failed */ -int ntfs_inode_get_times(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; @@ -1250,7 +1456,7 @@ int ntfs_inode_get_times(const char *path __attribute__((unused)), ret = -ERANGE; } ntfs_attr_put_search_ctx(ctx); - } + } return (ret ? ret : -errno); } @@ -1268,22 +1474,21 @@ int ntfs_inode_get_times(const char *path __attribute__((unused)), * -1 if there were an error (described by errno) */ -int ntfs_inode_set_times(const char *path __attribute__((unused)), - const char *value, size_t size, - int flags, ntfs_inode *ni) +int ntfs_inode_set_times(ntfs_inode *ni, const char *value, size_t size, + int flags) { ntfs_attr_search_ctx *ctx; STANDARD_INFORMATION *std_info; FILE_NAME_ATTR *fn; const u64 *times; - le64 now; + ntfs_time now; int cnt; int ret; ret = -1; if ((size >= 8) && !(flags & XATTR_CREATE)) { times = (const u64*)value; - now = utc2ntfs(time((time_t*)NULL)); + now = ntfs_current_time(); /* update the standard information attribute */ ctx = ntfs_attr_get_search_ctx(ni, NULL); if (ctx) { @@ -1296,16 +1501,31 @@ int ntfs_inode_set_times(const char *path __attribute__((unused)), std_info = (STANDARD_INFORMATION *)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); /* - * Do not mark times dirty to avoid - * overwriting them when the inode is closed. + * Mark times set to avoid overwriting + * them when the inode is closed. + * The inode structure must also be updated + * (with loss of precision) because of cacheing. + * TODO : use NTFS precision in inode, and + * return sub-second times in getattr() */ + set_nino_flag(ni, TimesSet); std_info->creation_time = cpu_to_le64(times[0]); - if (size >= 16) + ni->creation_time + = std_info->creation_time; + if (size >= 16) { std_info->last_data_change_time = cpu_to_le64(times[1]); - if (size >= 24) + ni->last_data_change_time + = std_info->last_data_change_time; + } + if (size >= 24) { std_info->last_access_time = cpu_to_le64(times[2]); + ni->last_access_time + = std_info->last_access_time; + } std_info->last_mft_change_time = now; + ni->last_mft_change_time = now; ntfs_inode_mark_dirty(ctx->ntfs_ino); + NInoFileNameSetDirty(ni); /* update the file names attributes */ ntfs_attr_reinit_search_ctx(ctx); @@ -1315,10 +1535,6 @@ int ntfs_inode_set_times(const char *path __attribute__((unused)), 0, NULL, 0, ctx)) { fn = (FILE_NAME_ATTR*)((u8 *)ctx->attr + le16_to_cpu(ctx->attr->value_offset)); - /* - * Do not mark times dirty to avoid - * overwriting them when the inode is closed. - */ fn->creation_time = cpu_to_le64(times[0]); if (size >= 16) diff --git a/source/libntfs/inode.h b/source/libntfs/inode.h index 3abc878c..5a6f7da6 100644 --- a/source/libntfs/inode.h +++ b/source/libntfs/inode.h @@ -32,6 +32,7 @@ typedef struct _ntfs_inode ntfs_inode; #include "layout.h" #include "support.h" #include "volume.h" +#include "ntfstime.h" /** * enum ntfs_inode_state_bits - @@ -49,7 +50,8 @@ typedef enum { NI_FileNameDirty, /* 1: FILE_NAME attributes need to be updated in the index. */ NI_v3_Extensions, /* 1: JPA v3.x extensions present. */ - NI_TimesDirty, /* 1: Times need to be updated */ + NI_TimesSet, /* 1: Use times which were set */ + NI_KnownSize, /* 1: Set if sizes are meaningful */ } ntfs_inode_state_bits; #define test_nino_flag(ni, flag) test_bit(NI_##flag, (ni)->state) @@ -135,8 +137,11 @@ struct _ntfs_inode { * These two fields are used to sync filename index and guaranteed to be * correct, however value in index itself maybe wrong (windows itself * do not update them properly). + * For directories, they hold the index size, provided the + * flag KnownSize is set. */ - s64 data_size; /* Data size of unnamed DATA attribute. */ + s64 data_size; /* Data size of unnamed DATA attribute + (or INDEX_ROOT for directories) */ s64 allocated_size; /* Allocated size stored in the filename index. (NOTE: Equal to allocated size of the unnamed data attribute for normal or @@ -149,10 +154,10 @@ struct _ntfs_inode { * STANDARD_INFORMATION attribute and used to sync it and FILE_NAME * attribute in the index. */ - time_t creation_time; - time_t last_data_change_time; - time_t last_mft_change_time; - time_t last_access_time; + ntfs_time creation_time; + ntfs_time last_data_change_time; + ntfs_time last_mft_change_time; + ntfs_time last_access_time; /* NTFS 3.x extensions added by JPA */ /* only if NI_v3_Extensions is set in state */ le32 owner_id; @@ -177,6 +182,19 @@ extern ntfs_inode *ntfs_inode_allocate(ntfs_volume *vol); extern ntfs_inode *ntfs_inode_open(ntfs_volume *vol, const MFT_REF mref); extern int ntfs_inode_close(ntfs_inode *ni); +extern int ntfs_inode_close_in_dir(ntfs_inode *ni, ntfs_inode *dir_ni); + +#if CACHE_NIDATA_SIZE + +struct CACHED_GENERIC; + +extern int ntfs_inode_real_close(ntfs_inode *ni); +extern void ntfs_inode_invalidate(ntfs_volume *vol, const MFT_REF mref); +extern void ntfs_inode_nidata_free(const struct CACHED_GENERIC *cached); +extern int ntfs_inode_nidata_hash(const struct CACHED_GENERIC *item); + +#endif + extern ntfs_inode *ntfs_extent_inode_open(ntfs_inode *base_ni, const MFT_REF mref); @@ -195,9 +213,13 @@ extern int ntfs_inode_free_space(ntfs_inode *ni, int size); extern int ntfs_inode_badclus_bad(u64 mft_no, ATTR_RECORD *a); -extern int ntfs_inode_get_times(const char *path, char *value, - size_t size, ntfs_inode *ni); -extern int ntfs_inode_set_times(const char *path, const char *value, - size_t size, int flags, ntfs_inode *ni); +extern int ntfs_inode_get_times(ntfs_inode *ni, char *value, size_t size); + +extern int ntfs_inode_set_times(ntfs_inode *ni, const char *value, + size_t size, int flags); + +/* debugging */ +#define debug_double_inode(num, type) +#define debug_cached_inode(ni) #endif /* defined _NTFS_INODE_H */ diff --git a/source/libntfs/lcnalloc.c b/source/libntfs/lcnalloc.c index ae678430..e84d2431 100644 --- a/source/libntfs/lcnalloc.c +++ b/source/libntfs/lcnalloc.c @@ -473,7 +473,7 @@ done_zones_check: ntfs_log_trace("Switching zone.\n"); pass = 1; if (rlpos) { - LCN tc = tc = rl[rlpos - 1].lcn + + LCN tc = rl[rlpos - 1].lcn + rl[rlpos - 1].length + NTFS_LCNALLOC_SKIP; if (used_zone_pos) @@ -609,6 +609,42 @@ out: return ret; } +/* + * Basic cluster run free + * Returns 0 if successful + */ + +int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count) +{ + s64 nr_freed = 0; + int ret = -1; + + ntfs_log_trace("Entering.\n"); + ntfs_log_trace("Dealloc lcn 0x%llx, len 0x%llx.\n", + (long long)lcn, (long long)count); + + if (lcn >= 0) { + update_full_status(vol,lcn); + if (ntfs_bitmap_clear_run(vol->lcnbmp_na, lcn, + count)) { + ntfs_log_perror("Cluster deallocation failed " + "(%lld, %lld)", + (long long)lcn, + (long long)count); + goto out; + } + nr_freed += count; + } + ret = 0; +out: + vol->free_clusters += nr_freed; + if (vol->free_clusters > vol->nr_clusters) + ntfs_log_error("Too many free clusters (%lld > %lld)!", + (long long)vol->free_clusters, + (long long)vol->nr_clusters); + return ret; +} + /** * ntfs_cluster_free - free clusters on an ntfs volume * @vol: mounted ntfs volume on which to free the clusters diff --git a/source/libntfs/lcnalloc.h b/source/libntfs/lcnalloc.h index e87ca43a..cbf4c5cd 100644 --- a/source/libntfs/lcnalloc.h +++ b/source/libntfs/lcnalloc.h @@ -42,6 +42,7 @@ extern runlist *ntfs_cluster_alloc(ntfs_volume *vol, VCN start_vcn, s64 count, LCN start_lcn, const NTFS_CLUSTER_ALLOCATION_ZONES zone); extern int ntfs_cluster_free_from_rl(ntfs_volume *vol, runlist *rl); +extern int ntfs_cluster_free_basic(ntfs_volume *vol, s64 lcn, s64 count); extern int ntfs_cluster_free(ntfs_volume *vol, ntfs_attr *na, VCN start_vcn, s64 count); diff --git a/source/libntfs/logfile.c b/source/libntfs/logfile.c index 4a129cb4..277ad142 100644 --- a/source/libntfs/logfile.c +++ b/source/libntfs/logfile.c @@ -699,8 +699,8 @@ BOOL ntfs_is_logfile_clean(ntfs_attr *log_na, RESTART_PAGE_HEADER *rp) */ int ntfs_empty_logfile(ntfs_attr *na) { - /*s64 pos, count; - char buf[NTFS_BUF_SIZE];*/ + s64 pos, count; + char buf[NTFS_BUF_SIZE]; ntfs_log_trace("Entering.\n"); @@ -713,7 +713,7 @@ int ntfs_empty_logfile(ntfs_attr *na) return -1; } - /*memset(buf, -1, NTFS_BUF_SIZE); + memset(buf, -1, NTFS_BUF_SIZE); pos = 0; while ((count = na->data_size - pos) > 0) { @@ -729,7 +729,7 @@ int ntfs_empty_logfile(ntfs_attr *na) return -1; } pos += count; - }*/ + } NVolSetLogFileEmpty(na->ni->vol); diff --git a/source/libntfs/mft.c b/source/libntfs/mft.c index ce138d2e..e93c6646 100644 --- a/source/libntfs/mft.c +++ b/source/libntfs/mft.c @@ -1478,8 +1478,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); - set_nino_flag(ni, TimesDirty); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1781,8 +1780,7 @@ found_free_rec: ni->flags = 0; ni->creation_time = ni->last_data_change_time = ni->last_mft_change_time = - ni->last_access_time = time(NULL); - set_nino_flag(ni, TimesDirty); + ni->last_access_time = ntfs_current_time(); /* Update the default mft allocation position if it was used. */ if (!base_ni) vol->mft_data_pos = bit + 1; @@ -1862,7 +1860,11 @@ int ntfs_mft_record_free(ntfs_volume *vol, ntfs_inode *ni) } /* Throw away the now freed inode. */ +#if CACHE_NIDATA_SIZE + if (!ntfs_inode_real_close(ni)) { +#else if (!ntfs_inode_close(ni)) { +#endif vol->free_mft_records++; return 0; } diff --git a/source/libntfs/misc.c b/source/libntfs/misc.c index 8e662a6a..b2e17cbf 100644 --- a/source/libntfs/misc.c +++ b/source/libntfs/misc.c @@ -1,7 +1,6 @@ /** * misc.c : miscellaneous : * - dealing with errors in memory allocation - * - data caching * * Copyright (c) 2008 Jean-Pierre Andre * @@ -33,7 +32,6 @@ #endif #include "types.h" -#include "security.h" #include "misc.h" #include "logging.h" @@ -61,304 +59,3 @@ void *ntfs_malloc(size_t size) ntfs_log_perror("Failed to malloc %lld bytes", (long long)size); return p; } - -/* - * General functions to deal with LRU caches - * - * The cached data have to be organized in a structure in which - * the first fields must follow a mandatory pattern and further - * fields may contain any fixed size data. They are stored in an - * LRU list. - * - * A compare function must be provided for finding a wanted entry - * in the cache. Another function may be provided for invalidating - * an entry to facilitate multiple invalidation. - * - * These functions never return error codes. When there is a - * shortage of memory, data is simply not cached. - */ - -/* - * Fetch an entry from cache - * - * returns the cache entry, or NULL if not available - */ - -struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - - current = (struct CACHED_GENERIC*)NULL; - if (cache) { - /* - * Search sequentially in LRU list - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - while (current - && compare(current, wanted)) { - previous = current; - current = current->next; - } - if (current) - cache->hits++; - if (current && previous) { - /* - * found and not at head of list, unlink from current - * position and relink as head of list - */ - previous->next = current->next; - current->next = cache->most_recent_entry; - cache->most_recent_entry = current; - } - cache->reads++; - } - return (current); -} - -/* - * Enter an inode number into cache - * returns the cache entry or NULL if not possible - */ - -struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - struct CACHED_GENERIC *before; - - current = (struct CACHED_GENERIC*)NULL; - if (cache) { - - /* - * Search sequentially in LRU list to locate the end, - * and find out whether the entry is already in list - * As we normally go to the end, no statistics is - * kept. - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - before = (struct CACHED_GENERIC*)NULL; - while (current - && compare(current, item)) { - before = previous; - previous = current; - current = current->next; - } - - if (!current) { - /* - * Not in list, get a free entry or reuse the - * last entry, and relink as head of list - * Note : we assume at least three entries, so - * before, previous and first are different when - * an entry is reused. - */ - - if (cache->free_entry) { - current = cache->free_entry; - cache->free_entry = cache->free_entry->next; - if (item->varsize) { - current->variable = ntfs_malloc( - item->varsize); - } else - current->variable = (void*)NULL; - current->varsize = item->varsize; - } else { - before->next = (struct CACHED_GENERIC*)NULL; - current = previous; - if (item->varsize) { - if (current->varsize) - current->variable = realloc( - current->variable, - item->varsize); - else - current->variable = ntfs_malloc( - item->varsize); - } else { - if (current->varsize) - free(current->variable); - current->variable = (void*)NULL; - } - current->varsize = item->varsize; - } - current->next = cache->most_recent_entry; - cache->most_recent_entry = current; - memcpy(current->fixed, item->fixed, cache->fixed_size); - if (item->varsize) { - if (current->variable) { - memcpy(current->variable, - item->variable, item->varsize); - } else { - /* - * no more memory for variable part - * recycle entry in free list - * not an error, just uncacheable - */ - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - current = (struct CACHED_GENERIC*)NULL; - } - } else { - current->variable = (void*)NULL; - current->varsize = 0; - } - } - cache->writes++; - } - return (current); -} - -/* - * Invalidate entries in cache - * - * Several entries may have to be invalidated (at least for inodes - * associated to directories which have been renamed), a different - * compare function may be provided to select entries to invalidate - * - * Returns the number of deleted entries, this can be used by - * the caller to signal a cache corruption if the entry was - * supposed to be found. - */ - -int ntfs_invalidate_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare) -{ - struct CACHED_GENERIC *current; - struct CACHED_GENERIC *previous; - int count; - - current = (struct CACHED_GENERIC*)NULL; - count = 0; - if (cache) { - /* - * Search sequentially in LRU list - */ - current = cache->most_recent_entry; - previous = (struct CACHED_GENERIC*)NULL; - while (current) { - if (!compare(current, item)) { - /* - * Relink into free list - */ - if (previous) - previous->next = current->next; - else - cache->most_recent_entry = current->next; - current->next = cache->free_entry; - cache->free_entry = current; - if (current->variable) - free(current->variable); - current->varsize = 0; - if (previous) - current = previous->next; - else - current = cache->most_recent_entry; - count++; - } else { - previous = current; - current = current->next; - } - } - } - return (count); -} - -/* - * Free memory allocated to a cache - */ - -static void ntfs_free_cache(struct CACHE_HEADER *cache) -{ - struct CACHED_GENERIC *entry; - - if (cache) { - for (entry=cache->most_recent_entry; entry; entry=entry->next) - if (entry->variable) - free(entry->variable); - free(cache); - } -} - -/* - * Create a cache - * - * Returns the cache header, or NULL if the cache could not be created - */ - -static struct CACHE_HEADER *ntfs_create_cache(const char *name, - int full_item_size, int item_count) -{ - struct CACHE_HEADER *cache; - struct CACHED_GENERIC *p; - struct CACHED_GENERIC *q; - int i; - - cache = (struct CACHE_HEADER*) - ntfs_malloc(sizeof(struct CACHE_HEADER) - + item_count*full_item_size); - if (cache) { - cache->name = name; - cache->fixed_size = full_item_size - sizeof(struct CACHED_GENERIC); - cache->reads = 0; - cache->writes = 0; - cache->hits = 0; - /* chain the entries, and mark an invalid entry */ - cache->most_recent_entry = (struct CACHED_GENERIC*)NULL; - cache->free_entry = &cache->entry[0]; - p = &cache->entry[0]; - for (i=0; i<(item_count - 1); i++) { - q = (struct CACHED_GENERIC*)((char*)p + full_item_size); - p->next = q; - p->variable = (void*)NULL; - p->varsize = 0; - p = q; - } - /* special for the last entry */ - p->next = (struct CACHED_GENERIC*)NULL; - p->variable = (void*)NULL; - p->varsize = 0; - } - return (cache); -} - -/* - * Create all LRU caches - * - * No error return, if creation is not possible, cacheing will - * just be not available - */ - -void ntfs_create_lru_caches(ntfs_volume *vol) -{ -#if CACHE_INODE_SIZE - /* inode cache */ - vol->xinode_cache = ntfs_create_cache("inode", - sizeof(struct CACHED_INODE), CACHE_INODE_SIZE); -#endif - vol->securid_cache = ntfs_create_cache("securid", - sizeof(struct CACHED_SECURID), CACHE_SECURID_SIZE); -#if CACHE_LEGACY_SIZE - vol->legacy_cache = ntfs_create_cache("legacy", - sizeof(struct CACHED_PERMISSIONS_LEGACY), CACHE_LEGACY_SIZE); -#endif -} - -/* - * Free all LRU caches - */ - -void ntfs_free_lru_caches(ntfs_volume *vol) -{ -#if CACHE_INODE_SIZE - ntfs_free_cache(vol->xinode_cache); -#endif - ntfs_free_cache(vol->securid_cache); -#if CACHE_LEGACY_SIZE - ntfs_free_cache(vol->legacy_cache); -#endif -} diff --git a/source/libntfs/misc.h b/source/libntfs/misc.h index dbdee9a9..a03e964e 100644 --- a/source/libntfs/misc.h +++ b/source/libntfs/misc.h @@ -1,7 +1,6 @@ /* * misc.h : miscellaneous exports * - memory allocation - * - LRU caches * * Copyright (c) 2008 Jean-Pierre Andre * @@ -24,49 +23,6 @@ #ifndef _NTFS_MISC_H_ #define _NTFS_MISC_H_ -#include "volume.h" - -struct CACHED_GENERIC { - struct CACHED_GENERIC *next; - void *variable; - size_t varsize; - void *fixed[0]; -} ; - -struct CACHED_INODE { - struct CACHED_INODE *next; - const char *pathname; - size_t varsize; - /* above fields must match "struct CACHED_GENERIC" */ - u64 inum; -} ; - -typedef int (*cache_compare)(const struct CACHED_GENERIC *cached, - const struct CACHED_GENERIC *item); - -struct CACHE_HEADER { - const char *name; - struct CACHED_GENERIC *most_recent_entry; - struct CACHED_GENERIC *free_entry; - unsigned long reads; - unsigned long writes; - unsigned long hits; - int fixed_size; - struct CACHED_GENERIC entry[0]; -} ; - - /* cast to generic, avoiding gcc warnings */ -#define GENERIC(pstr) ((const struct CACHED_GENERIC*)(const void*)(pstr)) - -struct CACHED_GENERIC *ntfs_fetch_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *wanted, cache_compare compare); -struct CACHED_GENERIC *ntfs_enter_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare); -int ntfs_invalidate_cache(struct CACHE_HEADER *cache, - const struct CACHED_GENERIC *item, cache_compare compare); -void ntfs_create_lru_caches(ntfs_volume *vol); -void ntfs_free_lru_caches(ntfs_volume *vol); - void *ntfs_calloc(size_t size); void *ntfs_malloc(size_t size); diff --git a/source/libntfs/ntfs.c b/source/libntfs/ntfs.c index 85a01159..10451e47 100644 --- a/source/libntfs/ntfs.c +++ b/source/libntfs/ntfs.c @@ -70,7 +70,7 @@ static const devoptab_t devops_ntfs = { void ntfsInit (void) { static bool isInit = false; - + // Initialise ntfs-3g (if not already done so) if (!isInit) { isInit = true; @@ -83,9 +83,9 @@ void ntfsInit (void) #endif // Set our current local ntfs_set_locale(); - + } - + return; } @@ -97,14 +97,14 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) int partition_count = 0; sec_t part_lba = 0; int i; - + union { - u8 buffer[BYTES_PER_SECTOR]; + u8 buffer[512]; MASTER_BOOT_RECORD mbr; EXTENDED_BOOT_RECORD ebr; NTFS_BOOT_SECTOR boot; } sector; - + // Sanity check if (!interface) { errno = EINVAL; @@ -112,10 +112,10 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } if (!partitions) return 0; - + // Initialise ntfs-3g ntfsInit(); - + // Start the device and check that it is inserted if (!interface->startup()) { errno = EIO; @@ -124,7 +124,7 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) if (!interface->isInserted()) { return 0; } - + // Read the first sector on the device if (!interface->readSectors(0, 1, §or.buffer)) { errno = EIO; @@ -135,12 +135,12 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) if (sector.mbr.signature == MBR_SIGNATURE) { memcpy(&mbr, §or, sizeof(MASTER_BOOT_RECORD)); ntfs_log_debug("Valid Master Boot Record found\n"); - + // Search the partition table for all NTFS partitions (max. 4 primary partitions) for (i = 0; i < 4; i++) { partition = &mbr.partitions[i]; part_lba = le32_to_cpu(mbr.partitions[i].lba_start); - + ntfs_log_debug("Partition %i: %s, sector %d, type 0x%x\n", i + 1, partition->status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", part_lba, partition->type); @@ -151,11 +151,11 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) // Ignore empty partitions case PARTITION_TYPE_EMPTY: continue; - + // NTFS partition case PARTITION_TYPE_NTFS: { ntfs_log_debug("Partition %i: Claims to be NTFS\n", i + 1); - + // Read and validate the NTFS partition if (interface->readSectors(part_lba, 1, §or)) { if (sector.boot.oem_id == NTFS_OEM_ID) { @@ -168,28 +168,28 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) ntfs_log_debug("Partition %i: Invalid NTFS boot sector, not actually NTFS\n", i + 1); } } - + break; - + } - + // DOS 3.3+ or Windows 95 extended partition case PARTITION_TYPE_DOS33_EXTENDED: case PARTITION_TYPE_WIN95_EXTENDED: { ntfs_log_debug("Partition %i: Claims to be Extended\n", i + 1); - + // Walk the extended partition chain, finding all NTFS partitions within it sec_t ebr_lba = part_lba; sec_t next_erb_lba = 0; do { - + // Read and validate the extended boot record if (interface->readSectors(ebr_lba + next_erb_lba, 1, §or)) { if (sector.ebr.signature == EBR_SIGNATURE) { ntfs_log_debug("Logical Partition @ %d: type 0x%x\n", ebr_lba + next_erb_lba, sector.ebr.partition.status == PARTITION_STATUS_BOOTABLE ? "bootable (active)" : "non-bootable", sector.ebr.partition.type); - + // Get the start sector of the current partition // and the next extended boot record in the chain part_lba = ebr_lba + next_erb_lba + le32_to_cpu(sector.ebr.partition.lba_start); @@ -213,16 +213,16 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) next_erb_lba = 0; } } - + } while (next_erb_lba); - + break; - + } - + // Unknown or unsupported partition type default: { - + // Check if this partition has a valid NTFS boot record anyway, // it might be misrepresented due to a lazy partition editor if (interface->readSectors(part_lba, 1, §or)) { @@ -237,15 +237,15 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } } } - + break; - + } - + } - + } - + // Else it is assumed this device has no master boot record } else { ntfs_log_debug("No Master Boot Record was found!\n"); @@ -262,12 +262,12 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) } } } - + } // Shutdown the device /*interface->shutdown();*/ - + // Return the found partitions (if any) if (partition_count > 0) { *partitions = (sec_t*)ntfs_alloc(sizeof(sec_t) * partition_count); @@ -276,7 +276,7 @@ int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitions) return partition_count; } } - + return 0; } @@ -293,7 +293,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) // Initialise ntfs-3g ntfsInit(); - + // Find and mount all NTFS partitions on all known devices for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { disc = &discs[i]; @@ -302,7 +302,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) for (j = 0, k = 0; j < partition_count; j++) { // Find the next unused mount name - do { + do { sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); if (k >= NTFS_MAX_MOUNTS) { ntfs_free(partitions); @@ -320,12 +320,12 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) mount_count++; } } - + } ntfs_free(partitions); } } - + // Return the mounts (if any) if (mount_count > 0 && mounts) { *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); @@ -334,7 +334,7 @@ int ntfsMountAll (ntfs_md **mounts, u32 flags) return mount_count; } } - + return 0; } @@ -348,7 +348,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag int partition_count = 0; char name[128]; int i, j, k; - + // Sanity check if (!interface) { errno = EINVAL; @@ -357,7 +357,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag // Initialise ntfs-3g ntfsInit(); - + // Find the specified device then find and mount all NTFS partitions on it for (i = 0; discs[i].name != NULL && discs[i].interface != NULL; i++) { if (discs[i].interface == interface) { @@ -365,9 +365,9 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag partition_count = ntfsFindPartitions(disc->interface, &partitions); if (partition_count > 0 && partitions) { for (j = 0, k = 0; j < partition_count; j++) { - + // Find the next unused mount name - do { + do { sprintf(name, "%s%i", NTFS_MOUNT_PREFIX, k++); if (k >= NTFS_MAX_MOUNTS) { ntfs_free(partitions); @@ -375,7 +375,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag return -1; } } while (ntfsGetDevice(name, false)); - + // Mount the partition if (mount_count < NTFS_MAX_MOUNTS) { if (ntfsMount(name, disc->interface, partitions[j], CACHE_DEFAULT_PAGE_SIZE, CACHE_DEFAULT_PAGE_COUNT, flags)) { @@ -385,7 +385,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag mount_count++; } } - + } ntfs_free(partitions); } @@ -398,7 +398,7 @@ int ntfsMountDevice (const DISC_INTERFACE *interface, ntfs_md **mounts, u32 flag errno = ENODEV; return -1; } - + // Return the mounts (if any) if (mount_count > 0 && mounts) { *mounts = (ntfs_md*)ntfs_alloc(sizeof(ntfs_md) * mount_count); @@ -415,13 +415,13 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe { ntfs_vd *vd = NULL; gekko_fd *fd = NULL; - + // Sanity check if (!name || !interface) { errno = EINVAL; - return -1; + return false; } - + // Initialise ntfs-3g ntfsInit(); @@ -430,20 +430,20 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe errno = EADDRINUSE; return false; } - + // Check that we can at least read from this device if (!(interface->features & FEATURE_MEDIUM_CANREAD)) { errno = EPERM; return false; } - + // Allocate the volume descriptor vd = (ntfs_vd*)ntfs_alloc(sizeof(ntfs_vd)); if (!vd) { errno = ENOMEM; return false; } - + // Setup the volume descriptor vd->id = interface->ioType; vd->flags = 0; @@ -454,7 +454,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe vd->atime = ((flags & NTFS_UPDATE_ACCESS_TIMES) ? ATIME_ENABLED : ATIME_DISABLED); vd->showHiddenFiles = (flags & NTFS_SHOW_HIDDEN_FILES); vd->showSystemFiles = (flags & NTFS_SHOW_SYSTEM_FILES); - + // Allocate the device driver descriptor fd = (gekko_fd*)ntfs_alloc(sizeof(gekko_fd)); if (!fd) { @@ -462,7 +462,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe errno = ENOMEM; return false; } - + // Setup the device driver descriptor fd->interface = interface; fd->startSector = startSector; @@ -470,7 +470,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe fd->sectorCount = 0; fd->cachePageCount = cachePageCount; fd->cachePageSize = cachePageSize; - + // Allocate the device driver vd->dev = ntfs_device_alloc(name, 0, &ntfs_device_gekko_io_ops, fd); if (!vd->dev) { @@ -478,7 +478,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + // Build the mount flags if (flags & NTFS_READ_ONLY) vd->flags |= MS_RDONLY; @@ -493,10 +493,10 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe vd->flags |= MS_RECOVER; if (flags & NTFS_IGNORE_HIBERFILE) vd->flags |= MS_IGNORE_HIBERFILE; - + if (vd->flags & MS_RDONLY) ntfs_log_debug("Mounting \"%s\" as read-only\n", name); - + // Mount the device vd->vol = ntfs_device_mount(vd->dev, vd->flags); if (!vd->vol) { @@ -511,14 +511,14 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + // Initialise the volume descriptor if (ntfsInitVolume(vd)) { ntfs_umount(vd->vol, true); ntfs_free(vd); return false; } - + // Add the device to the devoptab table if (ntfsAddDevice(name, vd)) { ntfsDeinitVolume(vd); @@ -526,7 +526,7 @@ bool ntfsMount (const char *name, const DISC_INTERFACE *interface, sec_t startSe ntfs_free(vd); return false; } - + return true; } @@ -538,19 +538,19 @@ void ntfsUnmount (const char *name, bool force) vd = ntfsGetVolume(name); if (!vd) return; - + // Remove the device from the devoptab table ntfsRemoveDevice(name); - + // Deinitialise the volume descriptor ntfsDeinitVolume(vd); - + // Unmount the volume ntfs_umount(vd->vol, force); - + // Free the volume descriptor ntfs_free(vd); - + return; } @@ -560,13 +560,13 @@ const char *ntfsGetVolumeName (const char *name) //ntfs_attr *na = NULL; //ntfschar *ulabel = NULL; //char *volumeName = NULL; - + // Sanity check if (!name) { errno = EINVAL; return NULL; } - + // Get the devices volume descriptor vd = ntfsGetVolume(name); if (!vd) { @@ -574,15 +574,15 @@ const char *ntfsGetVolumeName (const char *name) return NULL; } return vd->vol->vol_name; -/* - +/* + // If the volume name has already been cached then just use that if (vd->name[0]) return vd->name; - + // Lock ntfsLock(vd); - + // Check if the volume name attribute exists na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); if (!na) { @@ -598,7 +598,7 @@ const char *ntfsGetVolumeName (const char *name) errno = ENOMEM; return false; } - + // Read the volume name if (ntfs_attr_pread(na, 0, na->data_size, ulabel) != na->data_size) { ntfs_free(ulabel); @@ -618,7 +618,7 @@ const char *ntfsGetVolumeName (const char *name) // If the volume name was read then cache it (for future fetches) if (volumeName) strcpy(vd->name, volumeName); - + // Close the volume name attribute if (na) ntfs_attr_close(na); @@ -626,12 +626,12 @@ const char *ntfsGetVolumeName (const char *name) // Clean up ntfs_free(volumeName); ntfs_free(ulabel); - + // Unlock ntfsUnlock(vd); - + return vd->name; -*/ +*/ } bool ntfsSetVolumeName (const char *name, const char *volumeName) @@ -640,13 +640,13 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) ntfs_attr *na = NULL; ntfschar *ulabel = NULL; int ulabel_len; - + // Sanity check if (!name) { errno = EINVAL; return false; } - + // Get the devices volume descriptor vd = ntfsGetVolume(name); if (!vd) { @@ -656,7 +656,7 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) // Lock ntfsLock(vd); - + // Convert the new volume name to unicode ulabel_len = ntfsLocalToUnicode(volumeName, &ulabel) * sizeof(ntfschar); if (ulabel_len < 0) { @@ -664,56 +664,56 @@ bool ntfsSetVolumeName (const char *name, const char *volumeName) errno = EINVAL; return false; } - + // Check if the volume name attribute exists na = ntfs_attr_open(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0); if (na) { - + // It does, resize it to match the length of the new volume name if (ntfs_attr_truncate(na, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + // Write the new volume name if (ntfs_attr_pwrite(na, 0, ulabel_len, ulabel) != ulabel_len) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + } else { - + // It doesn't, create it now if (ntfs_attr_add(vd->vol->vol_ni, AT_VOLUME_NAME, NULL, 0, (u8*)ulabel, ulabel_len)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + } // Reset the volumes name cache (as it has now been changed) vd->name[0] = '\0'; - + // Close the volume name attribute if (na) ntfs_attr_close(na); - + // Sync the volume node if (ntfs_inode_sync(vd->vol->vol_ni)) { ntfs_free(ulabel); ntfsUnlock(vd); return false; } - + // Clean up ntfs_free(ulabel); - + // Unlock ntfsUnlock(vd); - + return true; } diff --git a/source/libntfs/ntfs.h b/source/libntfs/ntfs.h index ff2af30b..d805f4c2 100644 --- a/source/libntfs/ntfs.h +++ b/source/libntfs/ntfs.h @@ -65,7 +65,7 @@ typedef struct _ntfs_md { * * @param INTERFACE The block device to search * @param PARTITIONS (out) A pointer to receive the array of partition start sectors - * + * * @return The number of entries in PARTITIONS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing PARTITIONS when finished with it */ @@ -76,7 +76,7 @@ extern int ntfsFindPartitions (const DISC_INTERFACE *interface, sec_t **partitio * * @param MOUNTS (out) A pointer to receive the array of mount descriptors * @param FLAGS Additional mounting flags. (see above) - * + * * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing MOUNTS when finished with it * @note All device caches are setup using default values (see above) @@ -89,7 +89,7 @@ extern int ntfsMountAll (ntfs_md **mounts, u32 flags); * @param INTERFACE The block device to mount. * @param MOUNTS (out) A pointer to receive the array of mount descriptors * @param FLAGS Additional mounting flags. (see above) - * + * * @return The number of entries in MOUNTS or -1 if an error occurred (see errno) * @note The caller is responsible for freeing MOUNTS when finished with it * @note The device cache is setup using default values (see above) @@ -103,9 +103,9 @@ extern int ntfsMountDevice (const DISC_INTERFACE* interface, ntfs_md **mounts, u * @param INTERFACE The block device to mount * @param STARTSECTOR The sector the partition begins at (see @ntfsFindPartitions) * @param CACHEPAGECOUNT The total number of pages in the device cache - * @param CACHEPAGESIZE The number of sectors per cache page + * @param CACHEPAGESIZE The number of sectors per cache page * @param FLAGS Additional mounting flags (see above) - * + * * @return True if mount was successful, false if no partition was found or an error occurred (see errno) * @note ntfsFindPartitions should be used first to locate the partitions start sector */ @@ -139,18 +139,6 @@ extern const char *ntfsGetVolumeName (const char *name); */ extern bool ntfsSetVolumeName (const char *name, const char *volumeName); -/* -typedef struct _FileInfo FileInfo; - -struct _FileInfo -{ - u64 offset[64]; - s64 sector[64]; - u64 count[64]; - u32 num; - u64 filesize; -}; -*/ typedef int (*_ntfs_frag_append_t)(void *ff, u32 offset, u32 sector, u32 count); int _NTFS_get_fragments (const char *path, _ntfs_frag_append_t append_fragment, void *callback_data); diff --git a/source/libntfs/ntfsdir.c b/source/libntfs/ntfsdir.c index 71828cc4..ad54b514 100644 --- a/source/libntfs/ntfsdir.c +++ b/source/libntfs/ntfsdir.c @@ -402,19 +402,22 @@ int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int nam // Convert the entry name to our current local if (ntfsUnicodeToLocal(name, name_len, &entry_name, 0) < 0) { - ntfs_free(entry); return -1; } + if(dir->first && dir->first->mref == FILE_root && + MREF(mref) == FILE_root && strcmp(entry_name, "..") == 0) + { + return 0; + } + // If this is not the parent or self directory reference if ((strcmp(entry_name, ".") != 0) && (strcmp(entry_name, "..") != 0)) { // Open the entry ntfs_inode *ni = ntfs_pathname_to_inode(dir->vd->vol, dir->ni, entry_name); - if (!ni) { - ntfs_free(entry); + if (!ni) return -1; - } // Double check that this entry can be emuerated (as described by the volume descriptor) if (((ni->flags & FILE_ATTR_HIDDEN) && !dir->vd->showHiddenFiles) || @@ -429,13 +432,14 @@ int ntfs_readdir_filler (DIR_ITER *dirState, const ntfschar *name, const int nam } // Allocate a new directory entry - entry = ntfs_alloc(sizeof(ntfs_dir_entry)); + entry = (ntfs_dir_entry *) ntfs_alloc(sizeof(ntfs_dir_entry)); if (!entry) return -1; // Setup the entry entry->name = entry_name; entry->next = NULL; + entry->mref = MREF(mref); // Link the entry to the directory if (!dir->first) { diff --git a/source/libntfs/ntfsdir.h b/source/libntfs/ntfsdir.h index 1b16d2f9..0550592f 100644 --- a/source/libntfs/ntfsdir.h +++ b/source/libntfs/ntfsdir.h @@ -30,6 +30,7 @@ */ typedef struct _ntfs_dir_entry { char *name; + u64 mref; struct _ntfs_dir_entry *next; } ntfs_dir_entry; diff --git a/source/libntfs/ntfsfile.c b/source/libntfs/ntfsfile.c index 262fb877..5db85168 100644 --- a/source/libntfs/ntfsfile.c +++ b/source/libntfs/ntfsfile.c @@ -65,8 +65,8 @@ void ntfsCloseFile (ntfs_file_state *file) // Sync the file (and its attributes) to disc if(file->write) { - ntfsSync(file->vd, file->ni); ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); + ntfsSync(file->vd, file->ni); } if (file->read) diff --git a/source/libntfs/ntfsfile.h b/source/libntfs/ntfsfile.h index 275c745a..8e5ffcc6 100644 --- a/source/libntfs/ntfsfile.h +++ b/source/libntfs/ntfsfile.h @@ -6,7 +6,7 @@ * * This program/include file 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; either version 2 of the License, or + * by the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program/include file is distributed in the hope that it will be @@ -43,7 +43,7 @@ typedef struct _ntfs_file_state { bool compressed; /* True if file data is compressed */ bool encrypted; /* True if file data is encryted */ off_t pos; /* Current position within the file (in bytes) */ - size_t len; /* Total length of the file (in bytes) */ + u64 len; /* Total length of the file (in bytes) */ struct _ntfs_file_state *prevOpenFile; /* The previous entry in a double-linked FILO list of open files */ struct _ntfs_file_state *nextOpenFile; /* The next entry in a double-linked FILO list of open files */ } ntfs_file_state; diff --git a/source/libntfs/ntfsfile_frag.c b/source/libntfs/ntfsfile_frag.c index 8fd75404..55823789 100644 --- a/source/libntfs/ntfsfile_frag.c +++ b/source/libntfs/ntfsfile_frag.c @@ -1,6 +1,6 @@ /** * ntfsfile.c - devoptab file routines for NTFS-based devices. - * + * Copyright (c) 2010 Miigotu * Copyright (c) 2009 Rhys "Shareese" Koedijk * Copyright (c) 2006 Michael "Chishm" Chisholm * @@ -58,22 +58,29 @@ void ntfsCloseFile (ntfs_file_state *file) // Special case fix ups for compressed and/or encrypted files if (file->compressed) - ntfs_attr_pclose(file->data_na); + ntfs_attr_pclose(file->data_na); +#ifdef HAVE_SETXATTR if (file->encrypted) ntfs_efs_fixup_attribute(NULL, file->data_na); - +#endif // Close the file data attribute (if open) if (file->data_na) ntfs_attr_close(file->data_na); - + // Sync the file (and its attributes) to disc if(file->write) + { + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME | NTFS_UPDATE_CTIME); ntfsSync(file->vd, file->ni); - + } + + if (file->read) + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); + // Close the file (if open) if (file->ni) ntfsCloseEntry(file->vd, file->ni); - + // Reset the file state file->ni = NULL; file->data_na = NULL; @@ -92,7 +99,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags ntfs_log_trace("fileStruct %p, path %s, flags %i, mode %i\n", fileStruct, path, flags, mode); ntfs_file_state* file = STATE(fileStruct); - + // Get the volume descriptor for this path file->vd = ntfsGetVolume(path); if (!file->vd) { @@ -122,7 +129,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags ntfsUnlock(file->vd); return -1; } - + // Try and find the file and (if found) ensure that it is not a directory file->ni = ntfsOpenEntry(file->vd, path); if (file->ni && (file->ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)) { @@ -131,10 +138,10 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EISDIR; return -1; } - + // Are we creating this file? if (flags & O_CREAT) { - + // The file SHOULD NOT already exist if (file->ni) { ntfsCloseEntry(file->vd, file->ni); @@ -142,7 +149,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EEXIST; return -1; } - + // Create the file file->ni = ntfsCreate(file->vd, path, S_IFREG, NULL); if (!file->ni) { @@ -151,14 +158,14 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags } } - + // Sanity check, the file should be open by now if (!file->ni) { ntfsUnlock(file->vd); r->_errno = ENOENT; return -1; } - + // Open the files data attribute file->data_na = ntfs_attr_open(file->ni, AT_DATA, AT_UNNAMED, 0); if(!file->data_na) { @@ -170,7 +177,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags // Determine if this files data is compressed and/or encrypted file->compressed = NAttrCompressed(file->data_na) || (file->ni->flags & FILE_ATTR_COMPRESSED); file->encrypted = NAttrEncrypted(file->data_na) || (file->ni->flags & FILE_ATTR_ENCRYPTED); - + // We cannot read/write encrypted files if (file->encrypted) { ntfs_attr_close(file->data_na); @@ -179,7 +186,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EACCES; return -1; } - + // Make sure we aren't trying to write to a read-only file if ((file->ni->flags & FILE_ATTR_READONLY) && file->write) { ntfs_attr_close(file->data_na); @@ -188,7 +195,7 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags r->_errno = EROFS; return -1; } - + // Truncate the file if requested if ((flags & O_TRUNC) && file->write) { if (ntfs_attr_truncate(file->data_na, 0)) { @@ -199,14 +206,16 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags return -1; } } - + // Set the files current position and length file->pos = 0; file->len = file->data_na->data_size; + ntfs_log_trace("file->len %d\n", file->len); + // Update file times ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); - + // Insert the file into the double-linked FILO list of open files if (file->vd->firstOpenFile) { file->nextOpenFile = file->vd->firstOpenFile; @@ -217,31 +226,31 @@ int ntfs_open_r (struct _reent *r, void *fileStruct, const char *path, int flags file->prevOpenFile = NULL; file->vd->firstOpenFile = file; file->vd->openFileCount++; - + // Unlock ntfsUnlock(file->vd); - + return (int)fileStruct; } int ntfs_close_r (struct _reent *r, int fd) { ntfs_log_trace("fd %p\n", fd); - + ntfs_file_state* file = STATE(fd); - + // Sanity check if (!file || !file->vd) { r->_errno = EBADF; return -1; } - + // Lock ntfsLock(file->vd); // Close the file ntfsCloseFile(file); - + // Remove the file from the double-linked FILO list of open files file->vd->openFileCount--; if (file->nextOpenFile) @@ -253,45 +262,45 @@ int ntfs_close_r (struct _reent *r, int fd) // Unlock ntfsUnlock(file->vd); - + return 0; } ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { ntfs_log_trace("fd %p, ptr %p, len %Li\n", fd, ptr, len); - + ntfs_file_state* file = STATE(fd); ssize_t written = 0; off_t old_pos = 0; - + // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { r->_errno = EINVAL; return -1; } - + // Short circuit cases where we don't actually have to do anything if (!ptr || len <= 0) { return 0; } - + // Lock ntfsLock(file->vd); - + // Check that we are allowed to write to this file if (!file->write) { ntfsUnlock(file->vd); r->_errno = EACCES; return -1; } - + // If we are in append mode, backup the current position and move to the end of the file if (file->append) { old_pos = file->pos; file->pos = file->len; } - + // Write to the files data atrribute while (len) { ssize_t ret = ntfs_attr_pwrite(file->data_na, file->pos, len, ptr); @@ -304,26 +313,22 @@ ssize_t ntfs_write_r (struct _reent *r, int fd, const char *ptr, size_t len) file->pos += ret; written += ret; } - + // If we are in append mode, restore the current position to were it was prior to this write if (file->append) { file->pos = old_pos; } - + // Mark the file for archiving (if we actually wrote something) if (written) file->ni->flags |= FILE_ATTR_ARCHIVE; - - // Update file times (if we actually wrote something) - if (written) - ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME); - + // Update the files data length file->len = file->data_na->data_size; - - // Unlock + + // Unlock ntfsUnlock(file->vd); - + return written; } @@ -346,6 +351,9 @@ int _NTFS_get_fragments (const char *path, if (fd != (int)file) return -12; + + + // Sanity check if (!file || !file->vd || !file->ni || !file->data_na) { //r->_errno = EINVAL; @@ -373,6 +381,7 @@ int _NTFS_get_fragments (const char *path, if (file->pos + len > file->len) { r->_errno = EOVERFLOW; len = file->len - file->pos; + ntfs_log_trace("EOVERFLOW"); } */ @@ -384,6 +393,7 @@ int _NTFS_get_fragments (const char *path, s64 ret = ntfs_attr_getfragments(file->data_na, file->pos, len, offset, append_fragment, callback_data); if (ret <= 0 || ret > len) { + ntfsUnlock(file->vd); //r->_errno = errno; ret_val = -14; if (ret < 0) ret_val = ret; @@ -401,6 +411,7 @@ int _NTFS_get_fragments (const char *path, ret_val = 0; /* + //ntfs_log_trace("file->pos: %d \n", (u32)file->pos); // Update file times (if we actually read something) if (read) ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_ATIME); @@ -523,7 +534,7 @@ int ntfs_ftruncate_r (struct _reent *r, int fd, off_t len) // Update file times (if we actually changed something) if (file->len != file->data_na->data_size) - ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_MCTIME); + ntfsUpdateTimes(file->vd, file->ni, NTFS_UPDATE_AMCTIME); // Update the files data length file->len = file->data_na->data_size; diff --git a/source/libntfs/ntfsinternal.c b/source/libntfs/ntfsinternal.c index b4d39491..fed15bce 100644 --- a/source/libntfs/ntfsinternal.c +++ b/source/libntfs/ntfsinternal.c @@ -34,7 +34,6 @@ #include #endif -#include "usbloader/usbstorage2.h" #include "ntfsinternal.h" #include "ntfsdir.h" #include "ntfsfile.h" @@ -42,11 +41,11 @@ #if defined(__wii__) #include #include - +#include const INTERFACE_ID ntfs_disc_interfaces[] = { { "sd", &__io_wiisd }, - { "usb", &__io_usbstorage2 }, + { "usb", &__io_usbstorage }, { "carda", &__io_gcsda }, { "cardb", &__io_gcsdb }, { NULL, NULL } @@ -77,7 +76,7 @@ int ntfsAddDevice (const char *name, void *deviceData) } // Allocate a devoptab for this device - dev = ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); + dev = (devoptab_t *) ntfs_alloc(sizeof(devoptab_t) + strlen(name) + 1); if (!dev) { errno = ENOMEM; return false; @@ -304,7 +303,7 @@ ntfs_inode *ntfsParseEntry (ntfs_vd *vd, const char *path, int reparseLevel) } // Get the target path of this entry - target = ntfs_make_symlink(path, ni, &attr_size); + target = ntfs_make_symlink(ni, path, &attr_size); if (!target) { ntfsCloseEntry(vd, ni); return NULL; @@ -549,7 +548,6 @@ int ntfsLink (ntfs_vd *vd, const char *old_path, const char *new_path) } // Update entry times - ntfsUpdateTimes(vd, ni, NTFS_UPDATE_CTIME); ntfsUpdateTimes(vd, dir_ni, NTFS_UPDATE_MCTIME); // Sync the entry to disc @@ -816,7 +814,7 @@ int ntfsUnicodeToLocal (const ntfschar *ins, const int ins_len, char **outs, int // do it manually by replacing non-ASCII characters with underscores if (!*outs || outs_len >= ins_len) { if (!*outs) { - *outs = ntfs_alloc(ins_len + 1); + *outs = (char *) ntfs_alloc(ins_len + 1); if (!*outs) { errno = ENOMEM; return -1; diff --git a/source/libntfs/ntfstime.h b/source/libntfs/ntfstime.h index e933b53c..426269d7 100644 --- a/source/libntfs/ntfstime.h +++ b/source/libntfs/ntfstime.h @@ -3,6 +3,7 @@ * * Copyright (c) 2005 Anton Altaparmakov * Copyright (c) 2005 Yura Pakhuchiy + * Copyright (c) 2010 Jean-Pierre Andre * * This program/include file is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as published @@ -26,44 +27,95 @@ #ifdef HAVE_TIME_H #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif +#ifdef HAVE_GETTIMEOFDAY +#include +#endif #include "types.h" +/* + * There are four times more conversions of internal representation + * to ntfs representation than any other conversion, so the most + * efficient internal representation is ntfs representation + * (with low endianness) + */ +typedef sle64 ntfs_time; + #define NTFS_TIME_OFFSET ((s64)(369 * 365 + 89) * 24 * 3600 * 10000000) /** - * ntfs2utc - Convert an NTFS time to Unix time + * ntfs2timespec - Convert an NTFS time to Unix time * @ntfs_time: An NTFS time in 100ns units since 1601 * * NTFS stores times as the number of 100ns intervals since January 1st 1601 at * 00:00 UTC. This system will not suffer from Y2K problems until ~57000AD. * - * Return: n A Unix time (number of seconds since 1970) + * Return: A Unix time (number of seconds since 1970, and nanoseconds) */ -static __inline__ time_t ntfs2utc(s64 ntfs_time) +static __inline__ struct timespec ntfs2timespec(ntfs_time ntfstime) { - return (sle64_to_cpu(ntfs_time) - (NTFS_TIME_OFFSET)) / 10000000; + struct timespec spec; + s64 cputime; + + cputime = sle64_to_cpu(ntfstime); + spec.tv_sec = (cputime - (NTFS_TIME_OFFSET)) / 10000000; + spec.tv_nsec = (cputime - (NTFS_TIME_OFFSET) + - (s64)spec.tv_sec*10000000)*100; + /* force zero nsec for overflowing dates */ + if ((spec.tv_nsec < 0) || (spec.tv_nsec > 999999999)) + spec.tv_nsec = 0; + return (spec); } /** - * utc2ntfs - Convert Linux time to NTFS time + * timespec2ntfs - Convert Linux time to NTFS time * @utc_time: Linux time to convert to NTFS * * Convert the Linux time @utc_time to its corresponding NTFS time. * * Linux stores time in a long at present and measures it as the number of - * 1-second intervals since 1st January 1970, 00:00:00 UTC. + * 1-second intervals since 1st January 1970, 00:00:00 UTC + * with a separated non-negative nanosecond value * - * NTFS uses Microsoft's standard time format which is stored in a s64 and is + * NTFS uses Microsoft's standard time format which is stored in a sle64 and is * measured as the number of 100 nano-second intervals since 1st January 1601, * 00:00:00 UTC. * - * Return: n An NTFS time (100ns units since Jan 1601) + * Return: An NTFS time (100ns units since Jan 1601) */ -static __inline__ s64 utc2ntfs(time_t utc_time) +static __inline__ ntfs_time timespec2ntfs(struct timespec spec) { - /* Convert to 100ns intervals and then add the NTFS time offset. */ - return cpu_to_sle64((s64)utc_time * 10000000 + NTFS_TIME_OFFSET); + s64 units; + + units = (s64)spec.tv_sec * 10000000 + + NTFS_TIME_OFFSET + spec.tv_nsec/100; + return (cpu_to_le64(units)); +} + +/* + * Return the current time in ntfs format + */ + +static __inline__ ntfs_time ntfs_current_time(void) +{ + struct timespec now; + +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_SYS_CLOCK_GETTIME) + clock_gettime(CLOCK_REALTIME, &now); +#elif defined(HAVE_GETTIMEOFDAY) + struct timeval microseconds; + + gettimeofday(µseconds, (struct timezone*)NULL); + now.tv_sec = microseconds.tv_sec; + now.tv_nsec = microseconds.tv_usec*1000; +#else + now.tv_sec = time((time_t*)NULL); + now.tv_nsec = 0; +#endif + return (timespec2ntfs(now)); } #endif /* _NTFS_NTFSTIME_H */ diff --git a/source/libntfs/object_id.c b/source/libntfs/object_id.c new file mode 100644 index 00000000..555dd137 --- /dev/null +++ b/source/libntfs/object_id.c @@ -0,0 +1,637 @@ +/** + * object_id.c - Processing of object ids + * + * This module is part of ntfs-3g library + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_ERRNO_H +#include +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_SYS_STAT_H +#include +#endif + +#ifdef HAVE_SETXATTR +#include +#endif + +#ifdef HAVE_SYS_SYSMACROS_H +#include +#endif + +#include "types.h" +#include "debug.h" +#include "attrib.h" +#include "inode.h" +#include "dir.h" +#include "volume.h" +#include "mft.h" +#include "index.h" +#include "lcnalloc.h" +#include "object_id.h" +#include "logging.h" +#include "misc.h" + +/* + * Endianness considerations + * + * According to RFC 4122, GUIDs should be printed with the most + * significant byte first, and the six fields be compared individually + * for ordering. RFC 4122 does not define the internal representation. + * + * Here we always copy disk images with no endianness change, + * and, for indexing, GUIDs are compared as if they were a sequence + * of four unsigned 32 bit integers. + * + * --------------------- begin from RFC 4122 ---------------------- + * Consider each field of the UUID to be an unsigned integer as shown + * in the table in section Section 4.1.2. Then, to compare a pair of + * UUIDs, arithmetically compare the corresponding fields from each + * UUID in order of significance and according to their data type. + * Two UUIDs are equal if and only if all the corresponding fields + * are equal. + * + * UUIDs, as defined in this document, can also be ordered + * lexicographically. For a pair of UUIDs, the first one follows the + * second if the most significant field in which the UUIDs differ is + * greater for the first UUID. The second precedes the first if the + * most significant field in which the UUIDs differ is greater for + * the second UUID. + * + * The fields are encoded as 16 octets, with the sizes and order of the + * fields defined above, and with each field encoded with the Most + * Significant Byte first (known as network byte order). Note that the + * field names, particularly for multiplexed fields, follow historical + * practice. + * + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_low | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | time_mid | time_hi_and_version | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |clk_seq_hi_res | clk_seq_low | node (0-1) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | node (2-5) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * ---------------------- end from RFC 4122 ----------------------- + */ + +typedef struct { + GUID object_id; +} OBJECT_ID_INDEX_KEY; + +typedef struct { + le64 file_id; + GUID birth_volume_id; + GUID birth_object_id; + GUID domain_id; +} OBJECT_ID_INDEX_DATA; // known as OBJ_ID_INDEX_DATA + +struct OBJECT_ID_INDEX { /* index entry in $Extend/$ObjId */ + INDEX_ENTRY_HEADER header; + OBJECT_ID_INDEX_KEY key; + OBJECT_ID_INDEX_DATA data; +} ; + +static ntfschar objid_index_name[] = { const_cpu_to_le16('$'), + const_cpu_to_le16('O') }; +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Set the index for a new object id + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +static int set_object_id_index(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *object_id) +{ + struct OBJECT_ID_INDEX indx; + u64 file_id_cpu; + le64 file_id; + le16 seqn; + + seqn = ni->mrec->sequence_number; + file_id_cpu = MK_MREF(ni->mft_no,le16_to_cpu(seqn)); + file_id = cpu_to_le64(file_id_cpu); + indx.header.data_offset = const_cpu_to_le16( + sizeof(INDEX_ENTRY_HEADER) + + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.data_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_DATA)); + indx.header.reservedV = const_cpu_to_le32(0); + indx.header.length = const_cpu_to_le16( + sizeof(struct OBJECT_ID_INDEX)); + indx.header.key_length = const_cpu_to_le16( + sizeof(OBJECT_ID_INDEX_KEY)); + indx.header.flags = const_cpu_to_le16(0); + indx.header.reserved = const_cpu_to_le16(0); + + memcpy(&indx.key.object_id,object_id,sizeof(GUID)); + + indx.data.file_id = file_id; + memcpy(&indx.data.birth_volume_id, + &object_id->birth_volume_id,sizeof(GUID)); + memcpy(&indx.data.birth_object_id, + &object_id->birth_object_id,sizeof(GUID)); + memcpy(&indx.data.domain_id, + &object_id->domain_id,sizeof(GUID)); + ntfs_index_ctx_reinit(xo); + return (ntfs_ie_add(xo,(INDEX_ENTRY*)&indx)); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Open the $Extend/$ObjId file and its index + * + * Return the index context if opened + * or NULL if an error occurred (errno tells why) + * + * The index has to be freed and inode closed when not needed any more. + */ + +static ntfs_index_context *open_object_id_index(ntfs_volume *vol) +{ + u64 inum; + ntfs_inode *ni; + ntfs_inode *dir_ni; + ntfs_index_context *xo; + + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$ObjId"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } + if (ni) { + xo = ntfs_index_ctx_get(ni, objid_index_name, 2); + if (!xo) { + ntfs_inode_close(ni); + } + } else + xo = (ntfs_index_context*)NULL; + return (xo); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Merge object_id data stored in the index into + * a full object_id struct. + * + * returns 0 if merging successful + * -1 if no data could be merged. This is generally not an error + */ + +static int merge_index_data(ntfs_inode *ni, + const OBJECT_ID_ATTR *objectid_attr, + OBJECT_ID_ATTR *full_objectid) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + ntfs_index_context *xo; + ntfs_inode *xoni; + int res; + + res = -1; + xo = open_object_id_index(ni->vol); + if (xo) { + memcpy(&key.object_id,objectid_attr,sizeof(GUID)); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + /* make sure inode numbers match */ + if (entry + && (MREF(le64_to_cpu(entry->data.file_id)) + == ni->mft_no)) { + memcpy(&full_objectid->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&full_objectid->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&full_objectid->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + res = 0; + } + } + xoni = xo->ni; + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Remove an object id index entry if attribute present + * + * Returns the size of existing object id + * (the existing object_d is returned) + * -1 if failure, explained by errno + */ + +static int remove_object_id_index(ntfs_attr *na, ntfs_index_context *xo, + OBJECT_ID_ATTR *old_attr) +{ + OBJECT_ID_INDEX_KEY key; + struct OBJECT_ID_INDEX *entry; + s64 size; + int ret; + + ret = na->data_size; + if (ret) { + /* read the existing object id attribute */ + size = ntfs_attr_pread(na, 0, sizeof(GUID), old_attr); + if (size >= (s64)sizeof(GUID)) { + memcpy(&key.object_id, + &old_attr->object_id,sizeof(GUID)); + size = sizeof(GUID); + if (!ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + entry = (struct OBJECT_ID_INDEX*)xo->entry; + memcpy(&old_attr->birth_volume_id, + &entry->data.birth_volume_id, + sizeof(GUID)); + memcpy(&old_attr->birth_object_id, + &entry->data.birth_object_id, + sizeof(GUID)); + memcpy(&old_attr->domain_id, + &entry->data.domain_id, + sizeof(GUID)); + size = sizeof(OBJECT_ID_ATTR); + if (ntfs_index_rm(xo)) + ret = -1; + } + } else { + ret = -1; + errno = ENODATA; + } + } + return (ret); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Update the object id and index + * + * The object_id attribute should have been created and the + * non-duplication of the GUID should have been checked before. + * + * Returns 0 if success + * -1 if failure, explained by errno + * If could not remove the existing index, nothing is done, + * If could not write the new data, no index entry is inserted + * If failed to insert the index, data is removed + */ + +static int update_object_id(ntfs_inode *ni, ntfs_index_context *xo, + const OBJECT_ID_ATTR *value, size_t size) +{ + OBJECT_ID_ATTR old_attr; + ntfs_attr *na; + int oldsize; + int written; + int res; + + res = 0; + + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + + /* remove the existing index entry */ + oldsize = remove_object_id_index(na,xo,&old_attr); + if (oldsize < 0) + res = -1; + else { + /* resize attribute */ + res = ntfs_attr_truncate(na, (s64)sizeof(GUID)); + /* write the object_id in attribute */ + if (!res && value) { + written = (int)ntfs_attr_pwrite(na, + (s64)0, (s64)sizeof(GUID), + &value->object_id); + if (written != (s64)sizeof(GUID)) { + ntfs_log_error("Failed to update " + "object id\n"); + errno = EIO; + res = -1; + } + } + /* write index part if provided */ + if (!res + && ((size < sizeof(OBJECT_ID_ATTR)) + || set_object_id_index(ni,xo,value))) { + /* + * If cannot index, try to remove the object + * id and log the error. There will be an + * inconsistency if removal fails. + */ + ntfs_attr_rm(na); + ntfs_log_error("Failed to index object id." + " Possible corruption.\n"); + } + } + ntfs_attr_close(na); + NInoSetDirty(ni); + } else + res = -1; + return (res); +} + +/* + * Add a (dummy) object id to an inode if it does not exist + * + * returns 0 if attribute was inserted (or already present) + * -1 if adding failed (explained by errno) + */ + +static int add_object_id(ntfs_inode *ni, int flags) +{ + int res; + u8 dummy; + + res = -1; /* default return */ + if (!ntfs_attr_exist(ni,AT_OBJECT_ID, AT_UNNAMED,0)) { + if (!(flags & XATTR_REPLACE)) { + /* + * no object id attribute : add one, + * apparently, this does not feed the new value in + * Note : NTFS version must be >= 3 + */ + if (ni->vol->major_ver >= 3) { + res = ntfs_attr_add(ni, AT_OBJECT_ID, + AT_UNNAMED, 0, &dummy, (s64)0); + NInoSetDirty(ni); + } else + errno = EOPNOTSUPP; + } else + errno = ENODATA; + } else { + if (flags & XATTR_CREATE) + errno = EEXIST; + else + res = 0; + } + return (res); +} + +#endif /* HAVE_SETXATTR */ + +/* + * Delete an object_id index entry + * + * Returns 0 if success + * -1 if failure, explained by errno + */ + +int ntfs_delete_object_id_index(ntfs_inode *ni) +{ + ntfs_index_context *xo; + ntfs_inode *xoni; + ntfs_attr *na; + OBJECT_ID_ATTR old_attr; + int res; + + res = 0; + na = ntfs_attr_open(ni, AT_OBJECT_ID, AT_UNNAMED, 0); + if (na) { + /* + * read the existing object id + * and un-index it + */ + xo = open_object_id_index(ni->vol); + if (xo) { + if (remove_object_id_index(na,xo,&old_attr) < 0) + res = -1; + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + ntfs_attr_close(na); + } + return (res); +} + +#ifdef HAVE_SETXATTR /* extended attributes interface required */ + +/* + * Get the ntfs object id into an extended attribute + * + * If present, the object_id from the attribute and the GUIDs + * from the index are returned (formatted as OBJECT_ID_ATTR) + * + * Returns the global size (can be 0, 16 or 64) + * and the buffer is updated if it is long enough + */ + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size) +{ + OBJECT_ID_ATTR full_objectid; + OBJECT_ID_ATTR *objectid_attr; + s64 attr_size; + int full_size; + + full_size = 0; /* default to no data and some error to be defined */ + if (ni) { + objectid_attr = (OBJECT_ID_ATTR*)ntfs_attr_readall(ni, + AT_OBJECT_ID,(ntfschar*)NULL, 0, &attr_size); + if (objectid_attr) { + /* restrict to only GUID present in attr */ + if (attr_size == sizeof(GUID)) { + memcpy(&full_objectid.object_id, + objectid_attr,sizeof(GUID)); + full_size = sizeof(GUID); + /* get data from index, if any */ + if (!merge_index_data(ni, objectid_attr, + &full_objectid)) { + full_size = sizeof(OBJECT_ID_ATTR); + } + if (full_size <= (s64)size) { + if (value) + memcpy(value,&full_objectid, + full_size); + else + errno = EINVAL; + } + } else { + /* unexpected size, better return unsupported */ + errno = EOPNOTSUPP; + full_size = 0; + } + free(objectid_attr); + } else + errno = ENODATA; + } + return (full_size ? (int)full_size : -errno); +} + +/* + * Set the object id from an extended attribute + * + * If the size is 64, the attribute and index are set. + * else if the size is not less than 16 only the attribute is set. + * The object id index is set accordingly. + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, + const char *value, size_t size, int flags) +{ + OBJECT_ID_INDEX_KEY key; + ntfs_inode *xoni; + ntfs_index_context *xo; + int res; + + res = 0; + if (ni && value && (size >= sizeof(GUID))) { + xo = open_object_id_index(ni->vol); + if (xo) { + /* make sure the GUID was not used somewhere */ + memcpy(&key.object_id, value, sizeof(GUID)); + if (ntfs_index_lookup(&key, + sizeof(OBJECT_ID_INDEX_KEY), xo)) { + ntfs_index_ctx_reinit(xo); + res = add_object_id(ni, flags); + if (!res) { + /* update value and index */ + res = update_object_id(ni,xo, + (const OBJECT_ID_ATTR*)value, + size); + } + } else { + /* GUID is present elsewhere */ + res = -1; + errno = EEXIST; + } + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } else { + res = -1; + } + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +/* + * Remove the object id + * + * Returns 0, or -1 if there is a problem + */ + +int ntfs_remove_ntfs_object_id(ntfs_inode *ni) +{ + int res; + int olderrno; + ntfs_attr *na; + ntfs_inode *xoni; + ntfs_index_context *xo; + int oldsize; + OBJECT_ID_ATTR old_attr; + + res = 0; + if (ni) { + /* + * open and delete the object id + */ + na = ntfs_attr_open(ni, AT_OBJECT_ID, + AT_UNNAMED,0); + if (na) { + /* first remove index (old object id needed) */ + xo = open_object_id_index(ni->vol); + if (xo) { + oldsize = remove_object_id_index(na,xo, + &old_attr); + if (oldsize < 0) { + res = -1; + } else { + /* now remove attribute */ + res = ntfs_attr_rm(na); + if (res + && (oldsize > (int)sizeof(GUID))) { + /* + * If we could not remove the + * attribute, try to restore the + * index and log the error. There + * will be an inconsistency if + * the reindexing fails. + */ + set_object_id_index(ni, xo, + &old_attr); + ntfs_log_error( + "Failed to remove object id." + " Possible corruption.\n"); + } + } + + xoni = xo->ni; + ntfs_index_entry_mark_dirty(xo); + NInoSetDirty(xoni); + ntfs_index_ctx_put(xo); + ntfs_inode_close(xoni); + } + olderrno = errno; + ntfs_attr_close(na); + /* avoid errno pollution */ + if (errno == ENOENT) + errno = olderrno; + } else { + errno = ENODATA; + res = -1; + } + NInoSetDirty(ni); + } else { + errno = EINVAL; + res = -1; + } + return (res ? -1 : 0); +} + +#endif /* HAVE_SETXATTR */ diff --git a/source/libntfs/object_id.h b/source/libntfs/object_id.h new file mode 100644 index 00000000..31af9fd8 --- /dev/null +++ b/source/libntfs/object_id.h @@ -0,0 +1,35 @@ +/* + * + * Copyright (c) 2008 Jean-Pierre Andre + * + */ + +/* + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OBJECT_ID_H +#define OBJECT_ID_H + +int ntfs_get_ntfs_object_id(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_object_id(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_object_id(ntfs_inode *ni); + +int ntfs_delete_object_id_index(ntfs_inode *ni); + +#endif /* OBJECT_ID_H */ diff --git a/source/libntfs/param.h b/source/libntfs/param.h new file mode 100644 index 00000000..f21bd653 --- /dev/null +++ b/source/libntfs/param.h @@ -0,0 +1,82 @@ +/* + * param.h - Parameter values for ntfs-3g + * + * Copyright (c) 2009 Jean-Pierre Andre + * + * This program/include file 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program/include file is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program (in the main directory of the NTFS-3G + * distribution in the file COPYING); if not, write to the Free Software + * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef _NTFS_PARAM_H +#define _NTFS_PARAM_H + +#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ +#define CACHE_NIDATA_SIZE 64 /* idata cache, zero or >= 3 and not too big */ +#define CACHE_LOOKUP_SIZE 64 /* lookup cache, zero or >= 3 and not too big */ +#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ +#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ + +#define FORCE_FORMAT_v1x 0 /* Insert security data as in NTFS v1.x */ +#define OWNERFROMACL 1 /* Get the owner from ACL (not Windows owner) */ + + /* default security sub-authorities */ +enum { + DEFSECAUTH1 = -1153374643, /* 3141592653 */ + DEFSECAUTH2 = 589793238, + DEFSECAUTH3 = 462843383, + DEFSECBASE = 10000 +}; + +/* + * Parameters for compression + */ + + /* default option for compression */ +#define DEFAULT_COMPRESSION FALSE + /* (log2 of) number of clusters in a compression block for new files */ +#define STANDARD_COMPRESSION_UNIT 4 + /* maximum cluster size for allowing compression for new files */ +#define MAX_COMPRESSION_CLUSTER_SIZE 4096 + +/* + * Permission checking modes for high level and low level + * + * The choices for high and low lowel are independent, they have + * no effect on the library + * + * Stick to the recommended values unless you understand the consequences + * on protection and performances. Use of cacheing is good for + * performances, but bad on security. + * + * Possible values for high level : + * 1 : no cache, kernel control (recommended) + * 4 : no cache, file system control + * 7 : no cache, kernel control for ACLs + * + * Possible values for low level : + * 2 : no cache, kernel control + * 3 : use kernel/fuse cache, kernel control + * 5 : no cache, file system control (recommended) + * 8 : no cache, kernel control for ACLs + * + * Use of options 7 and 8 requires a patch to fuse + * When Posix ACLs are selected in the configure options, a value + * of 6 is added in the mount report. + */ + +#define HPERMSCONFIG 1 +#define LPERMSCONFIG 5 + +#endif /* defined _NTFS_PARAM_H */ diff --git a/source/libntfs/reparse.c b/source/libntfs/reparse.c index 80a25acb..0f6360e1 100644 --- a/source/libntfs/reparse.c +++ b/source/libntfs/reparse.c @@ -1,8 +1,7 @@ /** * reparse.c - Processing of reparse points * - * This module is part of ntfs-3g library, but may also be - * integrated in tools running over Linux or Windows + * This module is part of ntfs-3g library * * Copyright (c) 2008-2009 Jean-Pierre Andre * @@ -89,12 +88,6 @@ struct SYMLINK_REPARSE_DATA { /* reparse data for symlinks */ char path_buffer[0]; /* above data assume this is char array */ } ; -struct INODE_STACK { - struct INODE_STACK *previous; - struct INODE_STACK *next; - ntfs_inode *ni; -} ; - struct REPARSE_INDEX { /* index entry in $Extend/$Reparse */ INDEX_ENTRY_HEADER header; REPARSE_INDEX_KEY key; @@ -123,7 +116,7 @@ static const ntfschar vol_junction_head[] = { } ; static ntfschar reparse_index_name[] = { const_cpu_to_le16('$'), - const_cpu_to_le16('R') }; + const_cpu_to_le16('R') }; static const char mappingdir[] = ".NTFS-3G/"; @@ -194,10 +187,10 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, if (entry) { found = &entry->key.file_name; if (lkup - && !ntfs_names_collate(find.attr.file_name, + && ntfs_names_are_equal(find.attr.file_name, find.attr.file_name_length, found->file_name, found->file_name_length, - 1, IGNORE_CASE, + IGNORE_CASE, vol->upcase, vol->upcase_len)) lkup = 0; if (!lkup) { @@ -205,9 +198,7 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, ntfschar *uname, * name found : * fix original name and return inode */ - lemref = *(le64*)((char*)found->file_name - - sizeof(INDEX_ENTRY_HEADER) - - sizeof(FILE_NAME_ATTR)); + lemref = entry->indexed_file; mref = le64_to_cpu(lemref); for (i=0; ifile_name_length; i++) uname[i] = found->file_name[i]; @@ -271,181 +262,90 @@ static char *search_absolute(ntfs_volume *vol, ntfschar *path, return (target); } -/* - * Stack the next inode in the path - * - * Returns the new top of stack - * or NULL (with stack unchanged) if there is a problem - */ - -static struct INODE_STACK *stack_inode(struct INODE_STACK *topni, - ntfschar *name, int len, BOOL fix) -{ - struct INODE_STACK *curni; - u64 inum; - - if (fix) - inum = ntfs_fix_file_name(topni->ni, name, len); - else - inum = ntfs_inode_lookup_by_name(topni->ni, name, len); - if (inum != (u64)-1) { - inum = MREF(inum); - curni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK)); - if (curni) { - curni->ni = ntfs_inode_open(topni->ni->vol, inum); - topni->next = curni; - curni->previous = topni; - curni->next = (struct INODE_STACK*)NULL; - } - } else - curni = (struct INODE_STACK*)NULL; - return (curni); -} - -/* - * Destack and close the current inode in the path - * - * Returns the new top of stack - * or NULL (with stack unchanged) if there is a problem - */ - -static struct INODE_STACK *pop_inode(struct INODE_STACK *topni) -{ - struct INODE_STACK *curni; - - curni = (struct INODE_STACK*)NULL; - if (topni->previous) { - if (!ntfs_inode_close(topni->ni)) { - curni = topni->previous; - free(topni); - curni->next = (struct INODE_STACK*)NULL; - } - } else { - /* ".." reached the root of fs */ - errno = ENOENT; - } - return (curni); -} - /* * Search for a symbolic link along the target path, * with the target defined as a relative path * + * Note : the path used to access the current inode, may be + * different from the one implied in the target definition, + * when an inode has names in several directories. + * * Returns the path translated to a Linux path * or NULL if the path is not valid */ -static char *search_relative(ntfs_volume *vol, ntfschar *path, int count, - const char *base, BOOL isdir) +static char *search_relative(ntfs_inode *ni, ntfschar *path, int count) { - struct INODE_STACK *topni; - struct INODE_STACK *curni; - char *target; - ntfschar *unicode; - int unisz; - int start; - int len; + char *target = (char*)NULL; + ntfs_inode *curni; + ntfs_inode *newni; + u64 inum; + int pos; + int lth; + BOOL ok; + int max = 32; /* safety */ - target = (char*)NULL; /* default return */ - topni = (struct INODE_STACK*)malloc(sizeof(struct INODE_STACK)); - if (topni) { - topni->ni = ntfs_inode_open(vol, FILE_root); - topni->previous = (struct INODE_STACK*)NULL; - topni->next = (struct INODE_STACK*)NULL; - } - if (topni && topni->ni) { - /* - * Process the base path - */ - unicode = (ntfschar*)NULL; - unisz = ntfs_mbstoucs(base, &unicode); - if ((unisz > 0) && unicode) { - start = 1; - do { - len = 0; - while (((start + len) < unisz) - && (unicode[start + len] - != const_cpu_to_le16('/'))) - len++; - curni = (struct INODE_STACK*)NULL; - if ((start + len) < unisz) { - curni = stack_inode(topni, - &unicode[start], len, FALSE); - if (curni) - topni = curni; - } else - curni = topni; - start += len + 1; - } while (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY) - && (start < unisz)); - free(unicode); - if (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY)) { - start = 0; - do { - len = 0; - while (((start + len) < count) - && (path[start + len] - != const_cpu_to_le16('\\'))) - len++; - curni = (struct INODE_STACK*)NULL; - if ((path[start] - == const_cpu_to_le16('.')) - && ((len == 1) - || ((len == 2) - && (path[start+1] - == const_cpu_to_le16('.'))))) { - /* leave the .. or . in the path */ - curni = topni; - if (len == 2) { - curni = pop_inode(topni); - if (curni) - topni = curni; - } - } else { - curni = stack_inode(topni, - &path[start], len, TRUE); - if (curni) - topni = curni; - } - if (topni->ni) { - start += len; - if (start < count) - path[start++] - = const_cpu_to_le16('/'); - } - } while (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY) - && (start < count)); - if (curni - && topni->ni - && (topni->ni->mrec->flags - & MFT_RECORD_IS_DIRECTORY - ? isdir : !isdir)) { - if (ntfs_ucstombs(path, count, - &target, 0) < 0) { - if (target) { - free(target); - target = (char*)NULL; + pos = 0; + ok = TRUE; + curni = ntfs_dir_parent_inode(ni); + while (curni && ok && (pos < (count - 1)) && --max) { + if ((count >= (pos + 2)) + && (path[pos] == const_cpu_to_le16('.')) + && (path[pos+1] == const_cpu_to_le16('\\'))) { + path[1] = const_cpu_to_le16('/'); + pos += 2; + } else { + if ((count >= (pos + 3)) + && (path[pos] == const_cpu_to_le16('.')) + &&(path[pos+1] == const_cpu_to_le16('.')) + && (path[pos+2] == const_cpu_to_le16('\\'))) { + path[2] = const_cpu_to_le16('/'); + pos += 3; + newni = ntfs_dir_parent_inode(curni); + if (curni != ni) + ntfs_inode_close(curni); + curni = newni; + if (!curni) + ok = FALSE; + } else { + lth = 0; + while (((pos + lth) < count) + && (path[pos + lth] != const_cpu_to_le16('\\'))) + lth++; + if (lth > 0) + inum = ntfs_fix_file_name(curni,&path[pos],lth); + else + inum = (u64)-1; + if (!lth + || ((curni != ni) + && ntfs_inode_close(curni)) + || (inum == (u64)-1)) + ok = FALSE; + else { + curni = ntfs_inode_open(ni->vol, MREF(inum)); + if (!curni) + ok = FALSE; + else { + if (ok && ((pos + lth) < count)) { + path[pos + lth] = const_cpu_to_le16('/'); + pos += lth + 1; + } else { + pos += lth; + if ((ni->mrec->flags ^ curni->mrec->flags) + & MFT_RECORD_IS_DIRECTORY) + ok = FALSE; + if (ntfs_inode_close(curni)) + ok = FALSE; } } } } } - do { - if (topni->ni) - ntfs_inode_close(topni->ni); - curni = topni; - topni = topni->previous; - free(curni); - } while (topni); + } + + if (ok && (ntfs_ucstombs(path, count, &target, 0) < 0)) { + free(target); // needed ? + target = (char*)NULL; } return (target); } @@ -492,6 +392,62 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) return (ret); } +/* + * Do some sanity checks on reparse data + * + * The only general check is about the size (at least the tag must + * be present) + * If the reparse data looks like a junction point or symbolic + * link, more checks can be done. + * + */ + +static BOOL valid_reparse_data(ntfs_inode *ni, + const REPARSE_POINT *reparse_attr, size_t size) +{ + BOOL ok; + unsigned int offs; + unsigned int lth; + const struct MOUNT_POINT_REPARSE_DATA *mount_point_data; + const struct SYMLINK_REPARSE_DATA *symlink_data; + + ok = ni && reparse_attr + && (size >= sizeof(REPARSE_POINT)) + && (((size_t)le16_to_cpu(reparse_attr->reparse_data_length) + + sizeof(REPARSE_POINT)) == size); + if (ok) { + switch (reparse_attr->reparse_tag) { + case IO_REPARSE_TAG_MOUNT_POINT : + mount_point_data = (const struct MOUNT_POINT_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(mount_point_data->subst_name_offset); + lth = le16_to_cpu(mount_point_data->subst_name_length); + /* consistency checks */ + if (!(ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + || ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct MOUNT_POINT_REPARSE_DATA) + + offs + lth)) > size)) + ok = FALSE; + break; + case IO_REPARSE_TAG_SYMLINK : + symlink_data = (const struct SYMLINK_REPARSE_DATA*) + reparse_attr->reparse_data; + offs = le16_to_cpu(symlink_data->subst_name_offset); + lth = le16_to_cpu(symlink_data->subst_name_length); + if ((size_t)((sizeof(REPARSE_POINT) + + sizeof(struct SYMLINK_REPARSE_DATA) + + offs + lth)) > size) + ok = FALSE; + break; + default : + break; + } + } + if (!ok) + errno = EINVAL; + return (ok); +} + /* * Check and translate the target of a junction point or * a full absolute symbolic link. @@ -507,16 +463,12 @@ static int ntfs_drive_letter(ntfs_volume *vol, ntfschar letter) * or NULL if there were some problem, as described by errno */ - static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, - int count, const char *path, BOOL isdir) + int count, const char *mnt_point, BOOL isdir) { char *target; char *fulltarget; - int i; int sz; - int level; - const char *p; char *q; enum { DIR_JUNCTION, VOL_JUNCTION, NO_JUNCTION } kind; @@ -554,19 +506,11 @@ static char *ntfs_get_fulllink(ntfs_volume *vol, ntfschar *junction, && !ntfs_drive_letter(vol, junction[4])) { target = search_absolute(vol,&junction[7],count - 7, isdir); if (target) { - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + strlen(target) + 1); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + strlen(target) + 2); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; i= 'a') && (target[0] <= 'z')) target[0] += 'A' - 'a'; - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + sizeof(mappingdir) + strlen(target) - 3); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; i 1) { - for (i=1; i= 'a') && (target[0] <= 'z')) target[0] += 'A' - 'a'; - level = 0; - for (p=path; *p; p++) - if (*p == '/') - level++; - fulltarget = (char*)ntfs_malloc(3*level - + sizeof(mappingdir) + strlen(target) - 3); + fulltarget = (char*)ntfs_malloc(strlen(mnt_point) + + sizeof(mappingdir) + strlen(target) + 1); if (fulltarget) { - fulltarget[0] = 0; - if (level > 1) { - for (i=1; ivol; reparse_attr = (REPARSE_POINT*)ntfs_attr_readall(ni, AT_REPARSE_POINT,(ntfschar*)NULL, 0, &attr_size); - if (reparse_attr && attr_size) { + if (reparse_attr && attr_size + && valid_reparse_data(ni, reparse_attr, attr_size)) { switch (reparse_attr->reparse_tag) { case IO_REPARSE_TAG_MOUNT_POINT : mount_point_data = (struct MOUNT_POINT_REPARSE_DATA*) reparse_attr->reparse_data; offs = le16_to_cpu(mount_point_data->subst_name_offset); lth = le16_to_cpu(mount_point_data->subst_name_length); - /* consistency checks */ - if (isdir - && ((le16_to_cpu(reparse_attr->reparse_data_length) - + 8) == attr_size) - && ((int)((sizeof(REPARSE_POINT) - + sizeof(struct MOUNT_POINT_REPARSE_DATA) - + offs + lth)) <= attr_size)) { - target = ntfs_get_fulllink(vol, - (ntfschar*)&mount_point_data->path_buffer[offs], - lth/2, org_path, isdir); - if (target) - bad = FALSE; - } + /* reparse data consistency has been checked */ + target = ntfs_get_fulllink(vol, + (ntfschar*)&mount_point_data->path_buffer[offs], + lth/2, mnt_point, isdir); + if (target) + bad = FALSE; break; case IO_REPARSE_TAG_SYMLINK : symlink_data = (struct SYMLINK_REPARSE_DATA*) @@ -836,44 +746,37 @@ char *ntfs_make_symlink(const char *org_path, else kind = REL_TARGET; p--; - /* consistency checks */ - if (((le16_to_cpu(reparse_attr->reparse_data_length) - + 8) == attr_size) - && ((int)((sizeof(REPARSE_POINT) - + sizeof(struct SYMLINK_REPARSE_DATA) - + offs + lth)) <= attr_size)) { - switch (kind) { - case FULL_TARGET : - if (!(symlink_data->flags - & const_cpu_to_le32(1))) { - target = ntfs_get_fulllink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; - case ABS_TARGET : - if (symlink_data->flags - & const_cpu_to_le32(1)) { - target = ntfs_get_abslink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; - case REL_TARGET : - if (symlink_data->flags - & const_cpu_to_le32(1)) { - target = ntfs_get_rellink(vol, - p, lth/2, - org_path, isdir); - if (target) - bad = FALSE; - } - break; + /* reparse data consistency has been checked */ + switch (kind) { + case FULL_TARGET : + if (!(symlink_data->flags + & const_cpu_to_le32(1))) { + target = ntfs_get_fulllink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; } + break; + case ABS_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_abslink(vol, + p, lth/2, + mnt_point, isdir); + if (target) + bad = FALSE; + } + break; + case REL_TARGET : + if (symlink_data->flags + & const_cpu_to_le32(1)) { + target = ntfs_get_rellink(ni, + p, lth/2); + if (target) + bad = FALSE; + } + break; } break; } @@ -984,8 +887,9 @@ static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, key.reparse_tag = *preparse_tag; /* danger on processors which require proper alignment ! */ memcpy(&key.file_id, &file_id, 8); - if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr)) - ret = ntfs_index_rm(xr); + if (!ntfs_index_lookup(&key, sizeof(REPARSE_INDEX_KEY), xr) + && ntfs_index_rm(xr)) + ret = -1; } else { ret = -1; errno = ENODATA; @@ -1005,10 +909,20 @@ static int remove_reparse_index(ntfs_attr *na, ntfs_index_context *xr, static ntfs_index_context *open_reparse_index(ntfs_volume *vol) { + u64 inum; ntfs_inode *ni; + ntfs_inode *dir_ni; ntfs_index_context *xr; - ni = ntfs_pathname_to_inode(vol, NULL, "$Extend/$Reparse"); + /* do not use path_name_to inode - could reopen root */ + dir_ni = ntfs_inode_open(vol, FILE_Extend); + ni = (ntfs_inode*)NULL; + if (dir_ni) { + inum = ntfs_inode_lookup_by_mbsname(dir_ni,"$Reparse"); + if (inum != (u64)-1) + ni = ntfs_inode_open(vol, inum); + ntfs_inode_close(dir_ni); + } if (ni) { xr = ntfs_index_ctx_get(ni, reparse_index_name, 2); if (!xr) { @@ -1133,8 +1047,7 @@ int ntfs_delete_reparse_index(ntfs_inode *ni) * and the buffer is updated if it is long enough */ -int ntfs_get_ntfs_reparse_data(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size) { REPARSE_POINT *reparse_attr; s64 attr_size; @@ -1168,9 +1081,8 @@ int ntfs_get_ntfs_reparse_data(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, + const char *value, size_t size, int flags) { int res; u8 dummy; @@ -1178,7 +1090,7 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), ntfs_index_context *xr; res = 0; - if (ni && value && (size >= 4)) { + if (ni && valid_reparse_data(ni, (const REPARSE_POINT*)value, size)) { xr = open_reparse_index(ni->vol); if (xr) { if (!ntfs_attr_exist(ni,AT_REPARSE_POINT, @@ -1194,9 +1106,11 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), AT_REPARSE_POINT, AT_UNNAMED,0,&dummy, (s64)0); - if (!res) - ni->flags |= + if (!res) { + ni->flags |= FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); + } NInoSetDirty(ni); } else { errno = EOPNOTSUPP; @@ -1237,8 +1151,7 @@ int ntfs_set_ntfs_reparse_data(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), - ntfs_inode *ni) +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni) { int res; int olderrno; @@ -1267,6 +1180,7 @@ int ntfs_remove_ntfs_reparse_data(const char *path __attribute__((unused)), if (!res) { ni->flags &= ~FILE_ATTR_REPARSE_POINT; + NInoFileNameSetDirty(ni); } else { /* * If we could not remove the diff --git a/source/libntfs/reparse.h b/source/libntfs/reparse.h index 91ede03e..35f4aa45 100644 --- a/source/libntfs/reparse.h +++ b/source/libntfs/reparse.h @@ -24,15 +24,15 @@ #ifndef REPARSE_H #define REPARSE_H -char *ntfs_make_symlink(const char *org_path, - ntfs_inode *ni, int *pattr_size); +char *ntfs_make_symlink(ntfs_inode *ni, const char *mnt_point, + int *pattr_size); BOOL ntfs_possible_symlink(ntfs_inode *ni); -int ntfs_get_ntfs_reparse_data(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_reparse_data(const char *path, const char *value, - size_t size, int flags, ntfs_inode *ni); -int ntfs_remove_ntfs_reparse_data(const char *path, ntfs_inode *ni); +int ntfs_get_ntfs_reparse_data(ntfs_inode *ni, char *value, size_t size); + +int ntfs_set_ntfs_reparse_data(ntfs_inode *ni, const char *value, + size_t size, int flags); +int ntfs_remove_ntfs_reparse_data(ntfs_inode *ni); int ntfs_delete_reparse_index(ntfs_inode *ni); diff --git a/source/libntfs/runlist.c b/source/libntfs/runlist.c index f81996bd..cea24672 100644 --- a/source/libntfs/runlist.c +++ b/source/libntfs/runlist.c @@ -117,18 +117,33 @@ static runlist_element *ntfs_rl_realloc(runlist_element *rl, int old_size, * * Returns the reallocated runlist * or NULL if reallocation was not possible (with errno set) + * the runlist is left unchanged if the reallocation fails */ -runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries) +runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries) { + runlist_element *newrl; int last; + int irl; - last = 0; - while (rl[last].length) - last++; - rl = ntfs_rl_realloc(rl,last+1,last+more_entries+1); - if (!rl) - errno = ENOMEM; + if (na->rl && rl) { + irl = (int)(rl - na->rl); + last = irl; + while (na->rl[last].length) + last++; + newrl = ntfs_rl_realloc(na->rl,last+1,last+more_entries+1); + if (!newrl) { + errno = ENOMEM; + rl = (runlist_element*)NULL; + } else + na->rl = newrl; + rl = &newrl[irl]; + } else { + ntfs_log_error("Cannot extend unmapped runlist"); + errno = EIO; + rl = (runlist_element*)NULL; + } return (rl); } @@ -764,7 +779,7 @@ runlist_element *ntfs_runlists_merge(runlist_element *drl, * two into one, if that is possible (we check for overlap and discard the new * runlist if overlap present before returning NULL, with errno = ERANGE). */ -runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, +static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, const ATTR_RECORD *attr, runlist_element *old_rl) { VCN vcn; /* Current vcn. */ @@ -1622,7 +1637,11 @@ int ntfs_rl_truncate(runlist **arl, const VCN start_vcn) if (!arl || !*arl) { errno = EINVAL; - ntfs_log_perror("rl_truncate error: arl: %p *arl: %p", arl, *arl); + if (!arl) + ntfs_log_perror("rl_truncate error: arl: %p", arl); + else + ntfs_log_perror("rl_truncate error:" + " arl: %p *arl: %p", arl, *arl); return -1; } diff --git a/source/libntfs/runlist.h b/source/libntfs/runlist.h index 43de53cc..4b73af9f 100644 --- a/source/libntfs/runlist.h +++ b/source/libntfs/runlist.h @@ -49,7 +49,8 @@ struct _runlist_element {/* In memory vcn to lcn mapping structure element. */ s64 length; /* Run length in clusters. */ }; -extern runlist_element *ntfs_rl_extend(runlist_element *rl, int more_entries); +extern runlist_element *ntfs_rl_extend(ntfs_attr *na, runlist_element *rl, + int more_entries); extern LCN ntfs_rl_vcn_to_lcn(const runlist_element *rl, const VCN vcn); diff --git a/source/libntfs/security.c b/source/libntfs/security.c index 38acce40..ecf3ca07 100644 --- a/source/libntfs/security.c +++ b/source/libntfs/security.c @@ -41,17 +41,18 @@ #ifdef HAVE_FCNTL_H #include #endif -#ifdef HAVE_SYS_STAT_H -#include -#endif #ifdef HAVE_SETXATTR #include #endif +#ifdef HAVE_SYS_STAT_H +#include +#endif #include #include #include +#include "param.h" #include "types.h" #include "layout.h" #include "attrib.h" @@ -60,6 +61,7 @@ #include "bitmap.h" #include "security.h" #include "acls.h" +#include "cache.h" #include "misc.h" /* @@ -712,6 +714,7 @@ static le32 entersecurityattr(ntfs_volume *vol, INDEX_ENTRY *entry; INDEX_ENTRY *next; ntfs_index_context *xsii; + int retries; ntfs_attr *na; int olderrno; @@ -756,10 +759,14 @@ static le32 entersecurityattr(ntfs_volume *vol, * All index blocks should be at least half full * so there always is a last entry but one, * except when creating the first entry in index root. - * A simplified version of next(), limited to - * current index node, could be used + * This was however found not to be true : chkdsk + * sometimes deletes all the (unused) keys in the last + * index block without rebalancing the tree. + * When this happens, a new search is restarted from + * the smallest key. */ keyid = const_cpu_to_le32(0); + retries = 0; while (entry) { next = ntfs_index_next(entry,xsii); if (next) { @@ -775,6 +782,20 @@ static le32 entersecurityattr(ntfs_volume *vol, size = le32_to_cpu(psii->datasize); } entry = next; + if (!entry && !keyid && !retries) { + /* search failed, retry from smallest key */ + ntfs_index_ctx_reinit(xsii); + found = !ntfs_index_lookup((char*)&keyid, + sizeof(SII_INDEX_KEY), xsii); + if (!found && (errno != ENOENT)) { + ntfs_log_perror("Index $SII is broken"); + } else { + /* restore errno */ + errno = olderrno; + entry = xsii->entry; + } + retries++; + } } } if (!keyid) { @@ -1080,7 +1101,6 @@ static int update_secur_descr(ntfs_volume *vol, /* mark node as dirty */ NInoSetDirty(ni); - ntfs_inode_sync(ni); /* useful ? */ return (res); } @@ -1105,7 +1125,7 @@ static int update_secur_descr(ntfs_volume *vol, * -1 if there is a problem */ -static int upgrade_secur_desc(ntfs_volume *vol, const char *path, +static int upgrade_secur_desc(ntfs_volume *vol, const char *attr, ntfs_inode *ni) { int attrsz; @@ -1116,11 +1136,11 @@ static int upgrade_secur_desc(ntfs_volume *vol, const char *path, /* * upgrade requires NTFS format v3.x * also refuse upgrading for special files + * whose number is less than FILE_first_user */ if ((vol->major_ver >= 3) - && (path[0] == '/') - && (path[1] != '$') && (path[1] != '\0')) { + && (ni->mft_no >= FILE_first_user)) { attrsz = ntfs_attr_size(attr); securid = setsecurityattr(vol, (const SECURITY_DESCRIPTOR_RELATIVE*)attr, @@ -1149,9 +1169,8 @@ static int upgrade_secur_desc(ntfs_volume *vol, const char *path, } } else res = -1; - /* mark node as dirty */ - NInoSetDirty(ni); - ntfs_inode_sync(ni); /* useful ? */ + /* mark node as dirty */ + NInoSetDirty(ni); } else res = 1; @@ -1788,8 +1807,7 @@ static char *retrievesecurityattr(ntfs_volume *vol, SII_INDEX_KEY id) * The returned descriptor is dynamically allocated and has to be freed */ -static char *getsecurityattr(ntfs_volume *vol, - const char *path, ntfs_inode *ni) +static char *getsecurityattr(ntfs_volume *vol, ntfs_inode *ni) { SII_INDEX_KEY securid; char *securattr; @@ -1815,8 +1833,8 @@ static char *getsecurityattr(ntfs_volume *vol, securattr = ntfs_attr_readall(ni, AT_SECURITY_DESCRIPTOR, AT_UNNAMED, 0, &readallsz); if (securattr && !ntfs_valid_descr(securattr, readallsz)) { - ntfs_log_error("Bad security descriptor for %s\n", - path); + ntfs_log_error("Bad security descriptor for inode %lld\n", + (long long)ni->mft_no); free(securattr); securattr = (char*)NULL; } @@ -1830,7 +1848,8 @@ static char *getsecurityattr(ntfs_volume *vol, * minimum rights, so that a real descriptor can * be created by chown or chmod */ - ntfs_log_error("No security descriptor found for %s\n",path); + ntfs_log_error("No security descriptor found for inode %lld\n", + (long long)ni->mft_no); securattr = ntfs_build_descr(0, 0, adminsid, adminsid); } return (securattr); @@ -1854,6 +1873,7 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, int groupperms; int mask; BOOL somegroup; + BOOL needgroups; mode_t perms; int i; @@ -1888,9 +1908,17 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, } else perms &= 07700; } else { - /* analyze designated users and get mask */ + /* + * analyze designated users, get mask + * and identify whether we need to check + * the group memberships. The groups are + * not needed when all groups have the + * same permissions as other for the + * requested modes. + */ userperms = -1; groupperms = -1; + needgroups = FALSE; mask = 7; for (i=pxdesc->acccnt-1; i>=0 ; i--) { pxace = &pxdesc->acl.ace[i]; @@ -1902,6 +1930,12 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, case POSIX_ACL_MASK : mask = pxace->perms & 7; break; + case POSIX_ACL_GROUP_OBJ : + case POSIX_ACL_GROUP : + if (((pxace->perms & mask) ^ perms) + & (request >> 6) & 7) + needgroups = TRUE; + break; default : break; } @@ -1909,6 +1943,8 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, /* designated users */ if (userperms >= 0) perms = (perms & 07000) + (userperms & mask); + else if (!needgroups) + perms &= 07007; else { /* owning group */ if (!(~(perms >> 3) & request & mask) @@ -1951,7 +1987,7 @@ static int access_check_posix(struct SECURITY_CONTEXT *scx, */ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode * ni, mode_t request) + ntfs_inode * ni, mode_t request) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -1977,7 +2013,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, perm = 0; /* default to no permission */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2017,7 +2053,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, && (perm >= 0) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, path, + upgrade_secur_desc(scx->vol, securattr, ni); /* * fetch owner and group for cacheing @@ -2051,9 +2087,8 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, * the caller is expected to issue a new call */ -int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni) +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size) { const SECURITY_DESCRIPTOR_RELATIVE *phead; struct POSIX_SECURITY *pxdesc; @@ -2076,7 +2111,7 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, if (cached) pxdesc = cached->pxdesc; else { - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); if (securattr) { @@ -2107,7 +2142,7 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { upgrade_secur_desc(scx->vol, - path, securattr, ni); + securattr, ni); } #if OWNERFROMACL uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); @@ -2185,13 +2220,10 @@ int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Do no use as mode of the file * * returns -1 if there is a problem - * - * This is only used for checking creation of DOS file names */ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, - mode_t request __attribute__((unused))) + ntfs_inode *ni, mode_t request) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -2203,7 +2235,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, gid_t gid; int perm; - if (!scx->mapping[MAPUSERS] || !scx->uid) + if (!scx->mapping[MAPUSERS] || (!scx->uid && !(request & S_IEXEC))) perm = 07777; else { /* check whether available in cache */ @@ -2216,7 +2248,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, perm = 0; /* default to no permission */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2248,7 +2280,7 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, && (perm >= 0) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, path, + upgrade_secur_desc(scx->vol, securattr, ni); /* * fetch owner and group for cacheing @@ -2267,14 +2299,28 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, } } if (perm >= 0) { - if (uid == scx->uid) - perm &= 07700; - else - if ((gid == scx->gid) - || groupmember(scx, scx->uid, gid)) - perm &= 07070; + if (!scx->uid) { + /* root access and execution */ + if (perm & 0111) + perm = 07777; else - perm &= 07007; + perm = 0; + } else + if (uid == scx->uid) + perm &= 07700; + else + /* + * avoid checking group membership + * when the requested perms for group + * are the same as perms for other + */ + if ((gid == scx->gid) + || ((((perm >> 3) ^ perm) + & (request >> 6) & 7) + && groupmember(scx, scx->uid, gid))) + perm &= 07070; + else + perm &= 07007; } } return (perm); @@ -2290,15 +2336,14 @@ static int ntfs_get_perm(struct SECURITY_CONTEXT *scx, * the caller is expected to issue a new call */ -int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size) { char *securattr; size_t outsize; outsize = 0; /* default to no data and no error */ - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { outsize = ntfs_attr_size(securattr); if (outsize <= size) { @@ -2315,8 +2360,7 @@ int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, */ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode * ni, - struct stat *stbuf) + ntfs_inode * ni, struct stat *stbuf) { const SECURITY_DESCRIPTOR_RELATIVE *phead; char *securattr; @@ -2343,7 +2387,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, perm = -1; /* default to error */ isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - securattr = getsecurityattr(scx->vol, path, ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) @@ -2379,7 +2423,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { upgrade_secur_desc(scx->vol, - path, securattr, ni); + securattr, ni); } #if OWNERFROMACL stbuf->st_uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); @@ -2420,8 +2464,7 @@ int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, */ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, - mode_t mode, BOOL isdir) + ntfs_inode *dir_ni, mode_t mode, BOOL isdir) { const struct CACHED_PERMISSIONS *cached; const SECURITY_DESCRIPTOR_RELATIVE *phead; @@ -2445,7 +2488,7 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, scx->umask,isdir); } } else { - securattr = getsecurityattr(scx->vol, dir_path, dir_ni); + securattr = getsecurityattr(scx->vol, dir_ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*) securattr; @@ -2475,7 +2518,7 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, if (!test_nino_flag(dir_ni, v3_Extensions) && (scx->vol->secure_flags & (1 << SECURITY_ADDSECURIDS))) { - upgrade_secur_desc(scx->vol, dir_path, + upgrade_secur_desc(scx->vol, securattr, dir_ni); /* * fetch owner and group for cacheing @@ -2503,8 +2546,8 @@ static struct POSIX_SECURITY *inherit_posix(struct SECURITY_CONTEXT *scx, */ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const char *dir_path, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir) + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir) { #if !FORCE_FORMAT_v1x const struct CACHED_SECURID *cached; @@ -2523,7 +2566,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, #if !FORCE_FORMAT_v1x - pxdesc = inherit_posix(scx, dir_path, dir_ni, mode, isdir); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); if (pxdesc) { /* check whether target securid is known in cache */ @@ -2586,7 +2629,7 @@ le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, - const char *dir_path, ntfs_inode *dir_ni, mode_t mode) + ntfs_inode *dir_ni, mode_t mode) { struct POSIX_SECURITY *pxdesc; char *newattr; @@ -2599,7 +2642,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, res = -1; isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); - pxdesc = inherit_posix(scx, dir_path, dir_ni, mode, isdir); + pxdesc = inherit_posix(scx, dir_ni, mode, isdir); if (pxdesc) { usid = ntfs_find_usid(scx->mapping[MAPUSERS],uid,(SID*)&defusid); gsid = ntfs_find_gsid(scx->mapping[MAPGROUPS],gid,(SID*)&defgsid); @@ -2613,7 +2656,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, if (newattr) { /* Adjust Windows read-only flag */ res = update_secur_descr(scx->vol, newattr, ni); - if (!res) { + if (!res && !isdir) { if (mode & S_IWUSR) ni->flags &= ~FILE_ATTR_READONLY; else @@ -2630,7 +2673,7 @@ int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, + (pxdesc->acccnt + pxdesc->defcnt)*sizeof(struct POSIX_ACE); ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif free(newattr); @@ -2802,10 +2845,13 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, res = update_secur_descr(scx->vol, newattr, ni); if (!res) { /* adjust Windows read-only flag */ - if (mode & S_IWUSR) - ni->flags &= ~FILE_ATTR_READONLY; - else - ni->flags |= FILE_ATTR_READONLY; + if (!isdir) { + if (mode & S_IWUSR) + ni->flags &= ~FILE_ATTR_READONLY; + else + ni->flags |= FILE_ATTR_READONLY; + NInoFileNameSetDirty(ni); + } /* update cache, for subsequent use */ if (test_nino_flag(ni, v3_Extensions)) { wanted.securid = ni->security_id; @@ -2828,7 +2874,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, #endif ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif } @@ -2851,8 +2897,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, * if not, errno tells why */ -BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni) +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni) { const struct CACHED_PERMISSIONS *cached; char *oldattr; @@ -2862,36 +2907,48 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, BOOL gotowner; int allowed; - gotowner = FALSE; /* default */ processuid = scx->uid; - /* get the owner, either from cache or from old attribute */ - cached = fetch_cache(scx, ni); - if (cached) { - uid = cached->uid; - gotowner = TRUE; - } else { - oldattr = getsecurityattr(scx->vol,path, ni); - if (oldattr) { -#if OWNERFROMACL - usid = ntfs_acl_owner(oldattr); -#else - const SECURITY_DESCRIPTOR_RELATIVE *phead; - - phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; - usid = (const SID*)&oldattr[le32_to_cpu(phead->owner)]; -#endif - uid = ntfs_find_user(scx->mapping[MAPUSERS],usid); - gotowner = TRUE; - free(oldattr); - } - } - allowed = FALSE; - if (gotowner) { /* TODO : use CAP_FOWNER process capability */ - if (!processuid || (processuid == uid)) - allowed = TRUE; - else - errno = EPERM; + /* + * Always allow for root + * Also always allow if no mapping has been defined + */ + if (!scx->mapping[MAPUSERS] || !processuid) + allowed = TRUE; + else { + gotowner = FALSE; /* default */ + /* get the owner, either from cache or from old attribute */ + cached = fetch_cache(scx, ni); + if (cached) { + uid = cached->uid; + gotowner = TRUE; + } else { + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + const SECURITY_DESCRIPTOR_RELATIVE *phead; + + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + usid = (const SID*)&oldattr + [le32_to_cpu(phead->owner)]; +#endif + uid = ntfs_find_user(scx->mapping[MAPUSERS], + usid); + gotowner = TRUE; + free(oldattr); + } + } + allowed = FALSE; + if (gotowner) { +/* TODO : use CAP_FOWNER process capability */ + if (!processuid || (processuid == uid)) + allowed = TRUE; + else + errno = EPERM; + } } return (allowed); } @@ -2908,9 +2965,9 @@ BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni) + int flags) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -2950,7 +3007,7 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, (const struct POSIX_ACL*)value,count,deflt); } } else { - oldattr = getsecurityattr(scx->vol,path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL @@ -3011,11 +3068,11 @@ int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Returns 0, or -1 if there is a problem which errno describes */ -int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, ntfs_inode *ni) +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name) { - return (ntfs_set_posix_acl(scx, path, name, - (const char*)NULL, 0, 0, ni)); + return (ntfs_set_posix_acl(scx, ni, name, + (const char*)NULL, 0, 0)); } #endif @@ -3026,11 +3083,8 @@ int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, - const char *path __attribute__((unused)), - const char *name __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags) { char *attr; int res; @@ -3068,7 +3122,7 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, legacy.varsize = 0; ntfs_invalidate_cache(scx->vol->legacy_cache, GENERIC(&legacy), - (cache_compare)leg_compare); + (cache_compare)leg_compare,0); } #endif free(attr); @@ -3091,8 +3145,7 @@ int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, * -1 on failure, with errno = EIO */ -int ntfs_set_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, mode_t mode) +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -3133,7 +3186,7 @@ int ntfs_set_mode(struct SECURITY_CONTEXT *scx, newpxdesc = (struct POSIX_SECURITY*)NULL; #endif } else { - oldattr = getsecurityattr(scx->vol,path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)oldattr; #if OWNERFROMACL @@ -3280,7 +3333,7 @@ int ntfs_sd_add_everyone(ntfs_inode *ni) */ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, + ntfs_inode *ni, int accesstype) /* access type required (S_Ixxx values) */ { int perm; @@ -3288,10 +3341,6 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, int allow; struct stat stbuf; -#if POSIXACLS - /* shortcut, use only if Posix ACLs in use */ - if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) return (1); -#endif /* * Always allow for root unless execution is requested. * (was checked by fuse until kernel 2.6.29) @@ -3303,7 +3352,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, || (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY)))) allow = 1; else { - perm = ntfs_get_perm(scx, path, ni, accesstype); + perm = ntfs_get_perm(scx, ni, accesstype); if (perm >= 0) { res = EACCES; switch (accesstype) { @@ -3330,7 +3379,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, break; case S_IWRITE + S_IEXEC + S_ISVTX: if (perm & S_ISVTX) { - if ((ntfs_get_owner_mode(scx,path,ni,&stbuf) >= 0) + if ((ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) && (stbuf.st_uid == scx->uid)) allow = 1; else @@ -3339,6 +3388,11 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, allow = ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); break; + case S_IREAD + S_IWRITE + S_IEXEC: + allow = ((perm & (S_IRUSR | S_IRGRP | S_IROTH)) != 0) + && ((perm & (S_IWUSR | S_IWGRP | S_IWOTH)) != 0) + && ((perm & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0); + break; default : res = EINVAL; allow = 0; @@ -3352,6 +3406,8 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, return (allow); } +#if 0 /* not needed any more */ + /* * Check whether user can access the parent directory * of a file in a specific way @@ -3364,7 +3420,7 @@ int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, * This is used for Posix ACL and checking creation of DOS file names */ -BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype) { int allow; @@ -3374,10 +3430,6 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, ntfs_inode *dir_ni; struct stat stbuf; -#if POSIXACLS - /* shortcut, use only if Posix ACLs in use */ - if (scx->vol->secure_flags & (1 << SECURITY_DEFAULT)) return (TRUE); -#endif allow = 0; dirpath = strdup(path); if (dirpath) { @@ -3387,7 +3439,7 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, *name = 0; dir_ni = ntfs_pathname_to_inode(scx->vol, NULL, dirpath); if (dir_ni) { - allow = ntfs_allowed_access(scx,dirpath, + allow = ntfs_allowed_access(scx, dir_ni, accesstype); ntfs_inode_close(dir_ni); /* @@ -3400,7 +3452,7 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, path); allow = FALSE; if (ni) { - allow = (ntfs_get_owner_mode(scx,path,ni,&stbuf) >= 0) + allow = (ntfs_get_owner_mode(scx,ni,&stbuf) >= 0) && (stbuf.st_uid == scx->uid); ntfs_inode_close(ni); } @@ -3411,14 +3463,16 @@ BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, return (allow); /* errno is set if not allowed */ } +#endif + /* * Define a new owner/group to a file * * returns zero if successful */ -int ntfs_set_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, uid_t uid, gid_t gid) +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid) { const SECURITY_DESCRIPTOR_RELATIVE *phead; const struct CACHED_PERMISSIONS *cached; @@ -3453,7 +3507,7 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, fileuid = 0; filegid = 0; mode = 0; - oldattr = getsecurityattr(scx->vol, path, ni); + oldattr = getsecurityattr(scx->vol, ni); if (oldattr) { isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) != const_cpu_to_le16(0); @@ -3533,6 +3587,121 @@ int ntfs_set_owner(struct SECURITY_CONTEXT *scx, return (res ? -1 : 0); } +/* + * Define new owner/group and mode to a file + * + * returns zero if successful + */ + +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid, const mode_t mode) +{ + const SECURITY_DESCRIPTOR_RELATIVE *phead; + const struct CACHED_PERMISSIONS *cached; + char *oldattr; + const SID *usid; + const SID *gsid; + uid_t fileuid; + uid_t filegid; + BOOL isdir; + int res; +#if POSIXACLS + const struct POSIX_SECURITY *oldpxdesc; + struct POSIX_SECURITY *newpxdesc = (struct POSIX_SECURITY*)NULL; + int pxsize; +#endif + + res = 0; + /* get the current owner and mode from cache or security attributes */ + oldattr = (char*)NULL; + cached = fetch_cache(scx,ni); + if (cached) { + fileuid = cached->uid; + filegid = cached->gid; +#if POSIXACLS + oldpxdesc = cached->pxdesc; + if (oldpxdesc) { + /* must copy before merging */ + pxsize = sizeof(struct POSIX_SECURITY) + + (oldpxdesc->acccnt + oldpxdesc->defcnt)*sizeof(struct POSIX_ACE); + newpxdesc = (struct POSIX_SECURITY*)malloc(pxsize); + if (newpxdesc) { + memcpy(newpxdesc, oldpxdesc, pxsize); + if (ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + } else + res = -1; + } +#endif + } else { + fileuid = 0; + filegid = 0; + oldattr = getsecurityattr(scx->vol, ni); + if (oldattr) { + isdir = (ni->mrec->flags & MFT_RECORD_IS_DIRECTORY) + != const_cpu_to_le16(0); + phead = (const SECURITY_DESCRIPTOR_RELATIVE*) + oldattr; + gsid = (const SID*) + &oldattr[le32_to_cpu(phead->group)]; +#if OWNERFROMACL + usid = ntfs_acl_owner(oldattr); +#else + usid = (const SID*) + &oldattr[le32_to_cpu(phead->owner)]; +#endif +#if POSIXACLS + newpxdesc = ntfs_build_permissions_posix(scx->mapping, oldattr, + usid, gsid, isdir); + if (!newpxdesc || ntfs_merge_mode_posix(newpxdesc, mode)) + res = -1; + else { + fileuid = ntfs_find_user(scx->mapping[MAPUSERS],usid); + filegid = ntfs_find_group(scx->mapping[MAPGROUPS],gsid); + } +#endif + free(oldattr); + } else + res = -1; + } + if (!res) { + /* check requested by root */ + /* or chgrp requested by owner to an owned group */ + if (!scx->uid + || ((((int)uid < 0) || (uid == fileuid)) + && ((gid == scx->gid) || groupmember(scx, scx->uid, gid)) + && (fileuid == scx->uid))) { + /* replace by the new usid and gsid */ + /* or reuse old gid and sid for cacheing */ + if ((int)uid < 0) + uid = fileuid; + if ((int)gid < 0) + gid = filegid; +#if POSIXACLS + res = ntfs_set_owner_mode(scx, ni, uid, gid, + mode, newpxdesc); +#else + res = ntfs_set_owner_mode(scx, ni, uid, gid, mode); +#endif + } else { + res = -1; /* neither owner nor root */ + errno = EPERM; + } + } else { + /* + * Should not happen : a default descriptor is generated + * by getsecurityattr() when there are none + */ + ntfs_log_error("File has no security descriptor\n"); + res = -1; + errno = EIO; + } +#if POSIXACLS + free(newpxdesc); +#endif + return (res ? -1 : 0); +} + /* * Build a security id for a descriptor inherited from * parent directory the Windows way @@ -3670,7 +3839,7 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT *scx, */ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, BOOL fordir) + ntfs_inode *dir_ni, BOOL fordir) { struct CACHED_PERMISSIONS *cached; char *parentattr; @@ -3693,7 +3862,7 @@ le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, * Note : if parent directory has no id, it is not cacheable */ if (!securid) { - parentattr = getsecurityattr(scx->vol, dir_path, dir_ni); + parentattr = getsecurityattr(scx->vol, dir_ni); if (parentattr) { securid = build_inherited_id(scx, parentattr, fordir); @@ -3799,14 +3968,13 @@ static int link_group_members(struct SECURITY_CONTEXT *scx) return (res); } - /* * Apply default single user mapping * returns zero if successful */ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, - const SID *usid) + uid_t uid, gid_t gid, const SID *usid) { struct MAPPING *usermapping; struct MAPPING *groupmapping; @@ -3824,10 +3992,10 @@ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, groupmapping = (struct MAPPING*)ntfs_malloc(sizeof(struct MAPPING)); if (groupmapping) { usermapping->sid = sid; - usermapping->xid = scx->uid; + usermapping->xid = uid; usermapping->next = (struct MAPPING*)NULL; groupmapping->sid = sid; - groupmapping->xid = scx->uid; + groupmapping->xid = gid; groupmapping->next = (struct MAPPING*)NULL; scx->mapping[MAPUSERS] = usermapping; scx->mapping[MAPGROUPS] = groupmapping; @@ -3836,7 +4004,6 @@ static int ntfs_do_default_mapping(struct SECURITY_CONTEXT *scx, } } return (res); - } /* @@ -3878,6 +4045,8 @@ static BOOL check_mapping(const struct MAPPING *usermapping, #endif +#if 0 /* not used any more */ + /* * Try and apply default single user mapping * returns zero if successful @@ -3894,12 +4063,13 @@ static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) res = -1; ni = ntfs_pathname_to_inode(scx->vol, NULL, "/."); if (ni) { - securattr = getsecurityattr(scx->vol,"/.",ni); + securattr = getsecurityattr(scx->vol, ni); if (securattr) { phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr; usid = (SID*)&securattr[le32_to_cpu(phead->owner)]; if (ntfs_is_user_sid(usid)) - res = ntfs_do_default_mapping(scx,usid); + res = ntfs_do_default_mapping(scx, + scx->uid, scx->gid, usid); free(securattr); } ntfs_inode_close(ni); @@ -3907,6 +4077,8 @@ static int ntfs_default_mapping(struct SECURITY_CONTEXT *scx) return (res); } +#endif + /* * Basic read from a user mapping file on another volume */ @@ -3939,7 +4111,8 @@ static int localread(void *fileid, char *buf, size_t size, off_t offs) * (failure should not be interpreted as an error) */ -int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef) { struct MAPLIST *item; struct MAPLIST *firstitem; @@ -3947,6 +4120,22 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) struct MAPPING *groupmapping; ntfs_inode *ni; int fd; + static struct { + u8 revision; + u8 levels; + be16 highbase; + be32 lowbase; + le32 level1; + le32 level2; + le32 level3; + le32 level4; + le32 level5; + } defmap = { + 1, 5, const_cpu_to_be16(0), const_cpu_to_be32(5), + const_cpu_to_le32(21), + const_cpu_to_le32(DEFSECAUTH1), const_cpu_to_le32(DEFSECAUTH2), + const_cpu_to_le32(DEFSECAUTH3), const_cpu_to_le32(DEFSECBASE) + } ; /* be sure not to map anything until done */ scx->mapping[MAPUSERS] = (struct MAPPING*)NULL; @@ -3986,9 +4175,10 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) firstitem = item; } } else { - /* no mapping file, try default mapping */ - if (scx->uid && scx->gid) { - if (!ntfs_default_mapping(scx)) + /* no mapping file, try a default mapping */ + if (allowdef) { + if (!ntfs_do_default_mapping(scx, + 0, 0, (const SID*)&defmap)) ntfs_log_info("Using default user mapping\n"); } } @@ -4002,8 +4192,7 @@ int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path) * The attribute is returned according to cpu endianness */ -int ntfs_get_ntfs_attrib(const char *path __attribute__((unused)), - char *value, size_t size, ntfs_inode *ni) +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size) { u32 attrib; size_t outsize; @@ -4035,9 +4224,8 @@ int ntfs_get_ntfs_attrib(const char *path __attribute__((unused)), * Returns 0, or -1 if there is a problem */ -int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)), - const char *value, size_t size, int flags, - ntfs_inode *ni) +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags) { u32 attrib; le32 settable; @@ -4073,6 +4261,7 @@ int ntfs_set_ntfs_attrib(const char *path __attribute__((unused)), if (!res) { ni->flags = (ni->flags & ~settable) | (cpu_to_le32(attrib) & settable); + NInoFileNameSetDirty(ni); NInoSetDirty(ni); } } else @@ -4490,7 +4679,7 @@ int ntfs_get_file_security(struct SECURITY_API *scapi, if (scapi && (scapi->magic == MAGIC_API)) { ni = ntfs_pathname_to_inode(scapi->security.vol, NULL, path); if (ni) { - attr = getsecurityattr(scapi->security.vol, path, ni); + attr = getsecurityattr(scapi->security.vol, ni); if (attr) { if (feedsecurityattr(attr,selection, buf,buflen,psize)) { @@ -4559,7 +4748,7 @@ int ntfs_set_file_security(struct SECURITY_API *scapi, NULL, path); if (ni) { oldattr = getsecurityattr(scapi->security.vol, - path, ni); + ni); if (oldattr) { if (mergesecurityattr( scapi->security.vol, @@ -4950,7 +5139,7 @@ struct SECURITY_API *ntfs_initialize_file_security(const char *device, scx->pseccache = &scapi->seccache; scx->vol->secure_flags = 0; /* accept no mapping and no $Secure */ - ntfs_build_mapping(scx,(const char*)NULL); + ntfs_build_mapping(scx,(const char*)NULL,TRUE); ntfs_open_secure(vol); } else { if (scapi) diff --git a/source/libntfs/security.h b/source/libntfs/security.h index eaa67b41..4f3b5c54 100644 --- a/source/libntfs/security.h +++ b/source/libntfs/security.h @@ -34,6 +34,9 @@ #define POSIXACLS 0 #endif +typedef u16 be16; +typedef u32 be32; + #if __BYTE_ORDER == __LITTLE_ENDIAN #define const_cpu_to_be16(x) ((((x) & 255L) << 8) + (((x) >> 8) & 255L)) #define const_cpu_to_be32(x) ((((x) & 255L) << 24) + (((x) & 0xff00L) << 8) \ @@ -79,6 +82,7 @@ struct CACHED_PERMISSIONS { struct CACHED_PERMISSIONS_LEGACY { struct CACHED_PERMISSIONS_LEGACY *next; + struct CACHED_PERMISSIONS_LEGACY *previous; void *variable; size_t varsize; /* above fields must match "struct CACHED_GENERIC" */ @@ -92,6 +96,7 @@ struct CACHED_PERMISSIONS_LEGACY { struct CACHED_SECURID { struct CACHED_SECURID *next; + struct CACHED_SECURID *previous; void *variable; size_t varsize; /* above fields must match "struct CACHED_GENERIC" */ @@ -238,28 +243,29 @@ extern int ntfs_sd_add_everyone(ntfs_inode *ni); extern le32 ntfs_security_hash(const SECURITY_DESCRIPTOR_RELATIVE *sd, const u32 len); -int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path); +int ntfs_build_mapping(struct SECURITY_CONTEXT *scx, const char *usermap_path, + BOOL allowdef); int ntfs_get_owner_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, struct stat*); -int ntfs_set_mode(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, mode_t mode); -BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni); -int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, const char *path, + ntfs_inode *ni, struct stat*); +int ntfs_set_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, mode_t mode); +BOOL ntfs_allowed_as_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni); +int ntfs_allowed_access(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, int accesstype); -BOOL ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, +BOOL old_ntfs_allowed_dir_access(struct SECURITY_CONTEXT *scx, const char *path, int accesstype); #if POSIXACLS le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, - uid_t uid, gid_t gid, const char *dir_path, - ntfs_inode *dir_ni, mode_t mode, BOOL isdir); + uid_t uid, gid_t gid, ntfs_inode *dir_ni, + mode_t mode, BOOL isdir); #else le32 ntfs_alloc_securid(struct SECURITY_CONTEXT *scx, uid_t uid, gid_t gid, mode_t mode, BOOL isdir); #endif -int ntfs_set_owner(struct SECURITY_CONTEXT *scx, - const char *path, ntfs_inode *ni, uid_t uid, gid_t gid); +int ntfs_set_owner(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + uid_t uid, gid_t gid); +int ntfs_set_ownmod(struct SECURITY_CONTEXT *scx, + ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #if POSIXACLS int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, @@ -269,7 +275,7 @@ int ntfs_set_owner_mode(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, mode_t mode); #endif le32 ntfs_inherited_id(struct SECURITY_CONTEXT *scx, - const char *dir_path, ntfs_inode *dir_ni, BOOL fordir); + ntfs_inode *dir_ni, BOOL fordir); int ntfs_open_secure(ntfs_volume *vol); void ntfs_close_secure(struct SECURITY_CONTEXT *scx); @@ -277,28 +283,25 @@ void ntfs_close_secure(struct SECURITY_CONTEXT *scx); int ntfs_set_inherited_posix(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, uid_t uid, gid_t gid, - const char *dir_path, ntfs_inode *dir_ni, mode_t mode); -int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni); -int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, + ntfs_inode *dir_ni, mode_t mode); +int ntfs_get_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name, char *value, size_t size); +int ntfs_set_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni); -int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, ntfs_inode *ni); + int flags); +int ntfs_remove_posix_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *name); #endif -int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, char *value, size_t size, - ntfs_inode *ni); -int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, const char *path, - const char *name, const char *value, size_t size, - int flags, ntfs_inode *ni); -int ntfs_get_ntfs_attrib(const char *path, - char *value, size_t size, ntfs_inode *ni); -int ntfs_set_ntfs_attrib(const char *path, - const char *value, size_t size, int flags, - ntfs_inode *ni); +int ntfs_get_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + char *value, size_t size); +int ntfs_set_ntfs_acl(struct SECURITY_CONTEXT *scx, ntfs_inode *ni, + const char *value, size_t size, int flags); + +int ntfs_get_ntfs_attrib(ntfs_inode *ni, char *value, size_t size); +int ntfs_set_ntfs_attrib(ntfs_inode *ni, + const char *value, size_t size, int flags); + /* * Security API for direct access to security descriptors diff --git a/source/libntfs/types.h b/source/libntfs/types.h index 12e58f29..3fafe8a7 100644 --- a/source/libntfs/types.h +++ b/source/libntfs/types.h @@ -1,5 +1,5 @@ /* - * types.h - Misc type definitions not related to on-disk structure. + * types.h - Misc type definitions not related to on-disk structure. * Originated from the Linux-NTFS project. * * Copyright (c) 2000-2004 Anton Altaparmakov @@ -87,8 +87,8 @@ typedef sle64 leLSN; * Cygwin has a collision between our BOOL and 's * As long as this file will be included after were fine. */ -#ifndef _WINDEF_H #ifndef GEKKO +#ifndef _WINDEF_H /** * enum BOOL - These are just to make the code more readable... */ @@ -112,8 +112,8 @@ typedef enum { ONE = 1, #endif } BOOL; -#endif /* GEKKO */ #endif /* defined _WINDEF_H */ +#endif /* defined GECKO */ /** * enum IGNORE_CASE_BOOL - diff --git a/source/libntfs/unistr.c b/source/libntfs/unistr.c index 22eb2c61..cc3a1b8d 100644 --- a/source/libntfs/unistr.c +++ b/source/libntfs/unistr.c @@ -132,37 +132,30 @@ BOOL ntfs_names_are_equal(const ntfschar *s1, size_t s1_len, TRUE; } -/** - * ntfs_names_collate - collate two Unicode names +/* + * ntfs_names_full_collate() fully collate two Unicode names + * * @name1: first Unicode name to compare * @name1_len: length of first Unicode name to compare * @name2: second Unicode name to compare * @name2_len: length of second Unicode name to compare - * @err_val: if @name1 contains an invalid character return this value * @ic: either CASE_SENSITIVE or IGNORE_CASE * @upcase: upcase table (ignored if @ic is CASE_SENSITIVE) * @upcase_len: upcase table size (ignored if @ic is CASE_SENSITIVE) * - * ntfs_names_collate() collates two Unicode names and returns: - * * -1 if the first name collates before the second one, * 0 if the names match, * 1 if the second name collates before the first one, or - * @err_val if an invalid character is found in @name1 during the comparison. * - * The following characters are considered invalid: '"', '*', '<', '>' and '?'. - * - * A few optimizations made by JPA */ - -int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, +int ntfs_names_full_collate(const ntfschar *name1, const u32 name1_len, const ntfschar *name2, const u32 name2_len, - const int err_val __attribute__((unused)), const IGNORE_CASE_BOOL ic, const ntfschar *upcase, const u32 upcase_len) { u32 cnt; - ntfschar c1, c2; + u16 c1, c2; + u16 u1, u2; #ifdef DEBUG if (!name1 || !name2 || (ic && (!upcase || !upcase_len))) { @@ -171,38 +164,70 @@ int ntfs_names_collate(const ntfschar *name1, const u32 name1_len, } #endif cnt = min(name1_len, name2_len); - /* JPA average loop count is 8 */ if (cnt > 0) { - if (ic) - /* JPA this loop in 76% cases */ + if (ic == CASE_SENSITIVE) { do { c1 = le16_to_cpu(*name1); name1++; c2 = le16_to_cpu(*name2); name2++; - if (c1 < upcase_len) - c1 = le16_to_cpu(upcase[c1]); - if (c2 < upcase_len) - c2 = le16_to_cpu(upcase[c2]); - } while ((c1 == c2) && --cnt); - else + } while (--cnt && (c1 == c2)); + u1 = c1; + u2 = c2; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + if ((u1 == u2) && cnt) + do { + u1 = le16_to_cpu(*name1); + name1++; + u2 = le16_to_cpu(*name2); + name2++; + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + if (c1 < c2) + return -1; + if (c1 > c2) + return 1; + } else { do { - /* JPA this loop in 24% cases */ - c1 = le16_to_cpu(*name1); + u1 = c1 = le16_to_cpu(*name1); name1++; - c2 = le16_to_cpu(*name2); + u2 = c2 = le16_to_cpu(*name2); name2++; - } while ((c1 == c2) && --cnt); - if (c1 < c2) + if (u1 < upcase_len) + u1 = le16_to_cpu(upcase[u1]); + if (u2 < upcase_len) + u2 = le16_to_cpu(upcase[u2]); + } while ((u1 == u2) && --cnt); + if (u1 < u2) + return -1; + if (u1 > u2) + return 1; + if (name1_len < name2_len) + return -1; + if (name1_len > name2_len) + return 1; + } + } else { + if (name1_len < name2_len) return -1; - if (c1 > c2) + if (name1_len > name2_len) return 1; } - if (name1_len < name2_len) - return -1; - if (name1_len == name2_len) - return 0; - return 1; + return 0; } /** @@ -264,7 +289,7 @@ int ntfs_ucsncmp(const ntfschar *s1, const ntfschar *s2, size_t n) int ntfs_ucsncasecmp(const ntfschar *s1, const ntfschar *s2, size_t n, const ntfschar *upcase, const u32 upcase_size) { - ntfschar c1, c2; + u16 c1, c2; size_t i; #ifdef DEBUG @@ -357,13 +382,28 @@ void ntfs_name_upcase(ntfschar *name, u32 name_len, const ntfschar *upcase, const u32 upcase_len) { u32 i; - ntfschar u; + u16 u; for (i = 0; i < name_len; i++) if ((u = le16_to_cpu(name[i])) < upcase_len) name[i] = upcase[u]; } +/** + * ntfs_name_locase - Map a Unicode name to its lowercase equivalent + */ +void ntfs_name_locase(ntfschar *name, u32 name_len, const ntfschar *locase, + const u32 locase_len) +{ + u32 i; + u16 u; + + if (locase) + for (i = 0; i < name_len; i++) + if ((u = le16_to_cpu(name[i])) < locase_len) + name[i] = locase[u]; +} + /** * ntfs_file_value_upcase - Convert a filename to upper case * @file_name_attr: @@ -381,31 +421,6 @@ void ntfs_file_value_upcase(FILE_NAME_ATTR *file_name_attr, file_name_attr->file_name_length, upcase, upcase_len); } -/** - * ntfs_file_values_compare - Which of two filenames should be listed first - * @file_name_attr1: - * @file_name_attr2: - * @err_val: - * @ic: - * @upcase: - * @upcase_len: - * - * Description... - * - * Returns: - */ -int ntfs_file_values_compare(const FILE_NAME_ATTR *file_name_attr1, - const FILE_NAME_ATTR *file_name_attr2, - const int err_val, const IGNORE_CASE_BOOL ic, - const ntfschar *upcase, const u32 upcase_len) -{ - return ntfs_names_collate((ntfschar*)&file_name_attr1->file_name, - file_name_attr1->file_name_length, - (ntfschar*)&file_name_attr2->file_name, - file_name_attr2->file_name_length, - err_val, ic, upcase, upcase_len); -} - /* NTFS uses Unicode (UTF-16LE [NTFS-3G uses UCS-2LE, which is enough for now]) for path names, but the Unicode code points need to be @@ -505,7 +520,7 @@ static int ntfs_utf16_to_utf8(const ntfschar *ins, const int ins_len, char *t; int i, size, ret = -1; - ntfschar halfpair; + int halfpair; halfpair = 0; if (!*outs) @@ -614,24 +629,26 @@ static int utf8_to_utf16_size(const char *s) while ((byte = *((const unsigned char *)s++))) { if (++count >= PATH_MAX) goto fail; - if (byte >= 0xF5) { - errno = EILSEQ; - goto out; - } - if (!*s) - break; - if (byte >= 0xC0) - s++; - if (!*s) - break; - if (byte >= 0xE0) - s++; - if (!*s) - break; - if (byte >= 0xF0) { - s++; - if (++count >= PATH_MAX) - goto fail; + if (byte >= 0xc0) { + if (byte >= 0xF5) { + errno = EILSEQ; + goto out; + } + if (!*s) + break; + if (byte >= 0xC0) + s++; + if (!*s) + break; + if (byte >= 0xE0) + s++; + if (!*s) + break; + if (byte >= 0xF0) { + s++; + if (++count >= PATH_MAX) + goto fail; + } } } ret = count; @@ -663,8 +680,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) } else if (byte < 0xc2) { goto fail; } else if (byte < 0xE0) { - if (strlen(s) < 2) - goto fail; if ((s[1] & 0xC0) == 0x80) { *wc = ((u32)(byte & 0x1F) << 6) | ((u32)(s[1] & 0x3F)); @@ -673,8 +688,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) goto fail; /* three-byte */ } else if (byte < 0xF0) { - if (strlen(s) < 3) - goto fail; if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80)) { *wc = ((u32)(byte & 0x0F) << 12) | ((u32)(s[1] & 0x3F) << 6) @@ -693,8 +706,6 @@ static int utf8_to_unicode(u32 *wc, const char *s) goto fail; /* four-byte */ } else if (byte < 0xF5) { - if (strlen(s) < 4) - goto fail; if (((s[1] & 0xC0) == 0x80) && ((s[2] & 0xC0) == 0x80) && ((s[3] & 0xC0) == 0x80)) { *wc = ((u32)(byte & 0x07) << 18) @@ -737,6 +748,7 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) #endif /* defined(__APPLE__) || defined(__DARWIN__) */ const char *t = ins; u32 wc; + BOOL allocated; ntfschar *outpos; int shorts, ret = -1; @@ -744,18 +756,30 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) if (shorts < 0) goto fail; + allocated = FALSE; if (!*outs) { *outs = ntfs_malloc((shorts + 1) * sizeof(ntfschar)); if (!*outs) goto fail; + allocated = TRUE; } outpos = *outs; while(1) { int m = utf8_to_unicode(&wc, t); - if (m < 0) - goto fail; + if (m <= 0) { + if (m < 0) { + /* do not leave space allocated if failed */ + if (allocated) { + free(*outs); + *outs = (ntfschar*)NULL; + } + goto fail; + } + *outpos++ = const_cpu_to_le16(0); + break; + } if (wc < 0x10000) *outpos++ = cpu_to_le16(wc); else { @@ -763,8 +787,6 @@ static int ntfs_utf8_to_utf16(const char *ins, ntfschar **outs) *outpos++ = cpu_to_le16((wc >> 10) + 0xd800); *outpos++ = cpu_to_le16((wc & 0x3ff) + 0xdc00); } - if (m == 0) - break; t += m; } @@ -1028,6 +1050,61 @@ err_out: return -1; } +/* + * Turn a UTF8 name uppercase + * + * Returns an allocated uppercase name which has to be freed by caller + * or NULL if there is an error (described by errno) + */ + +char *ntfs_uppercase_mbs(const char *low, + const ntfschar *upcase, u32 upcase_size) +{ + int size; + char *upp; + u32 wc; + int n; + const char *s; + char *t; + + size = strlen(low); + upp = (char*)ntfs_malloc(3*size + 1); + if (upp) { + s = low; + t = upp; + do { + n = utf8_to_unicode(&wc, s); + if (n > 0) { + if (wc < upcase_size) + wc = le16_to_cpu(upcase[wc]); + if (wc < 0x80) + *t++ = wc; + else if (wc < 0x800) { + *t++ = (0xc0 | ((wc >> 6) & 0x3f)); + *t++ = 0x80 | (wc & 0x3f); + } else if (wc < 0x10000) { + *t++ = 0xe0 | (wc >> 12); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } else { + *t++ = 0xf0 | ((wc >> 18) & 7); + *t++ = 0x80 | ((wc >> 12) & 63); + *t++ = 0x80 | ((wc >> 6) & 0x3f); + *t++ = 0x80 | (wc & 0x3f); + } + s += n; + } + } while (n > 0); + if (n < 0) { + free(upp); + upp = (char*)NULL; + errno = EILSEQ; + } + *t = 0; + } + return (upp); +} + /** * ntfs_upcase_table_build - build the default upcase table for NTFS * @uc: destination buffer where to store the built table @@ -1099,6 +1176,38 @@ void ntfs_upcase_table_build(ntfschar *uc, u32 uc_len) } } +/* + * Build a table for converting to lower case + * + * This is only meaningful when there is a single lower case + * character leading to an upper case one, and currently the + * only exception is the greek letter sigma which has a single + * upper case glyph (code U+03A3), but two lower case glyphs + * (code U+03C3 and U+03C2, the latter to be used at the end + * of a word). In the following implementation the upper case + * sigma will be lowercased as U+03C3. + */ + +ntfschar *ntfs_locase_table_build(const ntfschar *uc, u32 uc_cnt) +{ + ntfschar *lc; + u32 upp; + u32 i; + + lc = (ntfschar*)ntfs_malloc(uc_cnt*sizeof(ntfschar)); + if (lc) { + for (i=0; i' - 0x20)) | (1L << ('?' - 0x20)); - forbidden = (len == 0) || (le16_to_cpu(name[len-1]) == ' '); + forbidden = (len == 0) + || (le16_to_cpu(name[len-1]) == ' ') + || (le16_to_cpu(name[len-1]) == '.'); for (i=0; ivol_name); free(v->upcase); + if (v->locase) free(v->locase); free(v->attrdef); free(v); @@ -486,7 +489,14 @@ ntfs_volume *ntfs_volume_startup(struct ntfs_device *dev, unsigned long flags) ntfs_upcase_table_build(vol->upcase, vol->upcase_len * sizeof(ntfschar)); + /* Default with no locase table and case sensitive file names */ + vol->locase = (ntfschar*)NULL; + NVolSetCaseSensitive(vol); + /* by default, all files are shown and not marked hidden */ + NVolSetShowSysFiles(vol); + NVolSetShowHidFiles(vol); + NVolClearHideDotFiles(vol); if (flags & MS_RDONLY) NVolSetReadOnly(vol); @@ -760,6 +770,70 @@ out: return errno ? -1 : 0; } +/* + * Make sure a LOGGED_UTILITY_STREAM attribute named "$TXF_DATA" + * on the root directory is resident. + * When it is non-resident, the partition cannot be mounted on Vista + * (see http://support.microsoft.com/kb/974729) + * + * We take care to avoid this situation, however this can be a + * consequence of having used an older version (including older + * Windows version), so we had better fix it. + * + * Returns 0 if unneeded or successful + * -1 if there was an error, explained by errno + */ + +static int fix_txf_data(ntfs_volume *vol) +{ + void *txf_data; + s64 txf_data_size; + ntfs_inode *ni; + ntfs_attr *na; + int res; + + res = 0; + ntfs_log_debug("Loading root directory\n"); + ni = ntfs_inode_open(vol, FILE_root); + if (!ni) { + ntfs_log_perror("Failed to open root directory"); + res = -1; + } else { + /* Get the $TXF_DATA attribute */ + na = ntfs_attr_open(ni, AT_LOGGED_UTILITY_STREAM, TXF_DATA, 9); + if (na) { + if (NAttrNonResident(na)) { + /* + * Fix the attribute by truncating, then + * rewriting it. + */ + ntfs_log_debug("Making $TXF_DATA resident\n"); + txf_data = ntfs_attr_readall(ni, + AT_LOGGED_UTILITY_STREAM, + TXF_DATA, 9, &txf_data_size); + if (txf_data) { + if (ntfs_attr_truncate(na, 0) + || (ntfs_attr_pwrite(na, 0, + txf_data_size, txf_data) + != txf_data_size)) + res = -1; + free(txf_data); + } + if (res) + ntfs_log_error("Failed to make $TXF_DATA resident\n"); + else + ntfs_log_error("$TXF_DATA made resident\n"); + } + ntfs_attr_close(na); + } + if (ntfs_inode_close(ni)) { + ntfs_log_perror("Failed to close root"); + res = -1; + } + } + return (res); +} + /** * ntfs_device_mount - open ntfs volume * @dev: device to open @@ -1045,9 +1119,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; for (j = 0; j < (s32)u; j++) { - ntfschar uc = le16_to_cpu(vname[j]); + u16 uc = le16_to_cpu(vname[j]); if (uc > 0xff) - uc = (ntfschar)'_'; + uc = (u16)'_'; vol->vol_name[j] = (char)uc; } vol->vol_name[u] = '\0'; @@ -1111,6 +1185,9 @@ ntfs_volume *ntfs_device_mount(struct ntfs_device *dev, unsigned long flags) goto error_exit; } } + /* make $TXF_DATA resident if present on the root directory */ + if (!NVolReadOnly(vol) && fix_txf_data(vol)) + goto error_exit; return vol; io_error_exit: @@ -1126,6 +1203,58 @@ error_exit: return NULL; } +/* + * Set appropriate flags for showing NTFS metafiles + * or files marked as hidden. + * Not set in ntfs_mount() to avoid breaking existing tools. + */ + +int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, + BOOL hide_dot_files) +{ + int res; + + res = -1; + if (vol) { + NVolClearShowSysFiles(vol); + NVolClearShowHidFiles(vol); + NVolClearHideDotFiles(vol); + if (show_sys_files) + NVolSetShowSysFiles(vol); + if (show_hid_files) + NVolSetShowHidFiles(vol); + if (hide_dot_files) + NVolSetHideDotFiles(vol); + res = 0; + } + if (res) + ntfs_log_error("Failed to set file visibility\n"); + return (res); +} + +/* + * Set ignore case mode + */ + +int ntfs_set_ignore_case(ntfs_volume *vol) +{ + int res; + + res = -1; + if (vol && vol->upcase) { + vol->locase = ntfs_locase_table_build(vol->upcase, + vol->upcase_len); + if (vol->locase) { + NVolClearCaseSensitive(vol); + res = 0; + } + } + if (res) + ntfs_log_error("Failed to set ignore_case mode\n"); + return (res); +} + /** * ntfs_mount - open ntfs volume * @name: name of device/file to open @@ -1432,7 +1561,7 @@ error_exit: * * Return 0 if successful and -1 if not with errno set to the error code. */ -int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags) +int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags) { ATTR_RECORD *a; VOLUME_INFORMATION *c; diff --git a/source/libntfs/volume.h b/source/libntfs/volume.h index a0c71bdf..79193c53 100644 --- a/source/libntfs/volume.h +++ b/source/libntfs/volume.h @@ -42,10 +42,6 @@ #include #endif -#define CACHE_INODE_SIZE 32 /* inode cache, zero or >= 3 and not too big */ -#define CACHE_SECURID_SIZE 16 /* securid cache, zero or >= 3 and not too big */ -#define CACHE_LEGACY_SIZE 8 /* legacy cache size, zero or >= 3 and not too big */ - /* * Under Cygwin, DJGPP and FreeBSD we do not have MS_RDONLY, * so we define them ourselves. @@ -65,6 +61,7 @@ /* Forward declaration */ typedef struct _ntfs_volume ntfs_volume; +#include "param.h" #include "types.h" #include "support.h" #include "device.h" @@ -110,6 +107,10 @@ typedef enum { NV_ReadOnly, /* 1: Volume is read-only. */ NV_CaseSensitive, /* 1: Volume is mounted case-sensitive. */ NV_LogFileEmpty, /* 1: $logFile journal is empty. */ + NV_ShowSysFiles, /* 1: Show NTFS metafiles. */ + NV_ShowHidFiles, /* 1: Show files marked hidden. */ + NV_HideDotFiles, /* 1: Set hidden flag on dot files */ + NV_Compression, /* 1: allow compression */ } ntfs_volume_state_bits; #define test_nvol_flag(nv, flag) test_bit(NV_##flag, (nv)->state) @@ -128,6 +129,22 @@ typedef enum { #define NVolSetLogFileEmpty(nv) set_nvol_flag(nv, LogFileEmpty) #define NVolClearLogFileEmpty(nv) clear_nvol_flag(nv, LogFileEmpty) +#define NVolShowSysFiles(nv) test_nvol_flag(nv, ShowSysFiles) +#define NVolSetShowSysFiles(nv) set_nvol_flag(nv, ShowSysFiles) +#define NVolClearShowSysFiles(nv) clear_nvol_flag(nv, ShowSysFiles) + +#define NVolShowHidFiles(nv) test_nvol_flag(nv, ShowHidFiles) +#define NVolSetShowHidFiles(nv) set_nvol_flag(nv, ShowHidFiles) +#define NVolClearShowHidFiles(nv) clear_nvol_flag(nv, ShowHidFiles) + +#define NVolHideDotFiles(nv) test_nvol_flag(nv, HideDotFiles) +#define NVolSetHideDotFiles(nv) set_nvol_flag(nv, HideDotFiles) +#define NVolClearHideDotFiles(nv) clear_nvol_flag(nv, HideDotFiles) + +#define NVolCompression(nv) test_nvol_flag(nv, Compression) +#define NVolSetCompression(nv) set_nvol_flag(nv, Compression) +#define NVolClearCompression(nv) clear_nvol_flag(nv, Compression) + /* * NTFS version 1.1 and 1.2 are used by Windows NT4. * NTFS version 2.x is used by Windows 2000 Beta @@ -159,7 +176,7 @@ struct _ntfs_volume { ntfs_inode *vol_ni; /* ntfs_inode structure for FILE_Volume. */ u8 major_ver; /* Ntfs major version of volume. */ u8 minor_ver; /* Ntfs minor version of volume. */ - u16 flags; /* Bit array of VOLUME_* flags. */ + le16 flags; /* Bit array of VOLUME_* flags. */ u16 sector_size; /* Byte size of a sector. */ u8 sector_size_bits; /* Log(2) of the byte size of a sector. */ @@ -220,6 +237,9 @@ struct _ntfs_volume { FILE_UpCase. */ u32 upcase_len; /* Length in Unicode characters of the upcase table. */ + ntfschar *locase; /* Lower case equivalents of all 65536 2-byte + Unicode characters. Only if option + case_ignore is set. */ ATTR_DEF *attrdef; /* Attribute definitions. Obtained from FILE_AttrDef. */ @@ -235,6 +255,12 @@ struct _ntfs_volume { #if CACHE_INODE_SIZE struct CACHE_HEADER *xinode_cache; #endif +#if CACHE_NIDATA_SIZE + struct CACHE_HEADER *nidata_cache; +#endif +#if CACHE_LOOKUP_SIZE + struct CACHE_HEADER *lookup_cache; +#endif #if CACHE_SECURID_SIZE struct CACHE_HEADER *securid_cache; #endif @@ -261,14 +287,17 @@ extern int ntfs_version_is_supported(ntfs_volume *vol); extern int ntfs_volume_check_hiberfile(ntfs_volume *vol, int verbose); extern int ntfs_logfile_reset(ntfs_volume *vol); -extern int ntfs_volume_write_flags(ntfs_volume *vol, const u16 flags); +extern int ntfs_volume_write_flags(ntfs_volume *vol, const le16 flags); extern int ntfs_volume_error(int err); extern void ntfs_mount_error(const char *vol, const char *mntpoint, int err); extern int ntfs_volume_get_free_space(ntfs_volume *vol); +extern int ntfs_set_shown_files(ntfs_volume *vol, + BOOL show_sys_files, BOOL show_hid_files, BOOL hide_dot_files); extern int ntfs_set_locale(void); +extern int ntfs_set_ignore_case(ntfs_volume *vol); #endif /* defined _NTFS_VOLUME_H */ diff --git a/source/main.cpp b/source/main.cpp index c79c7117..9ed977a5 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -51,6 +51,7 @@ extern "C" #include "usbloader/usbstorage2.h" #include "memory/mem2.h" #include "lstub.h" +#include "usbloader/usbstorage2.h" extern bool geckoinit; extern bool textVideoInit; @@ -65,172 +66,19 @@ extern char headlessID[8]; FreeTypeGX *fontSystem=0; FreeTypeGX *fontClock=0; PartList partitions; - u8 dbvideo =0; -static void BootUpProblems() -{ - s32 ret2; - - // load main font from file, or default to built-in font - fontSystem = new FreeTypeGX(); - fontSystem->loadFont(NULL, font_ttf, font_ttf_size, 0); - fontSystem->setCompatibilityMode(FTGX_COMPATIBILITY_DEFAULT_TEVOP_GX_PASSCLR | FTGX_COMPATIBILITY_DEFAULT_VTXDESC_GX_NONE); - - GuiImageData bootimageData(gxlogo_png); - GuiImage bootimage(&bootimageData); - GuiText boottext(NULL, 20, (GXColor) {255, 255, 255, 255} - ); - boottext.SetPosition(200, 240-1.2*bootimage.GetHeight()/2+250); - bootimage.SetPosition(320-1.2*bootimage.GetWidth()/2, 240-1.2*bootimage.GetHeight()/2); - bootimage.SetScale(1.2); - - GuiImageData usbimageData(usbport_png); - GuiImage usbimage(&usbimageData); - usbimage.SetPosition(400,300); - usbimage.SetScale(.7); - usbimage.SetAlpha(200); - - time_t curtime; - time_t endtime = time(0) + 30; - do - { - /*ret2 = IOS_ReloadIOSsafe(249); - if (ret2 < 0) { - ret2 = IOS_ReloadIOSsafe(222); - SDCard_Init(); - load_ehc_module(); - SDCard_deInit(); - if(ret2 <0) { - boottext.SetText("ERROR: cIOS could not be loaded!"); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - sleep(5); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - }*/ - USBDevice_deInit(); - USBDevice_Init(); - ret2 = WBFS_Init(WBFS_DEVICE_USB); - if (ret2 >= 0) - { - boottext.SetText("Loading..."); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - break; - } - curtime = time(0); - boottext.SetTextf("Waiting for your slow USB Device: %i secs...", int(endtime-curtime)); - while(curtime == time(0)) - { - boottext.Draw(); - bootimage.Draw(); - if (endtime-curtime<15)usbimage.Draw(); - Menu_Render(); - } - } while((endtime-time(0)) > 0); - - /*if(ret2 < 0) { - boottext.SetText("ERROR: USB device could not be loaded!"); - usbimage.Draw(); - bootimage.Draw(); - boottext.Draw(); - Menu_Render(); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - }*/ - - ///delete font to load up custom if set - if(fontSystem) - { - delete fontSystem; - fontSystem = NULL; - } -} - - -unsigned int *xfb = NULL; - -void InitTextVideo () -{ - gprintf("\nInitTextVideo ()"); - if (textVideoInit) - { - gprintf("...0"); - return; - } - dbvideo=1; - VIDEO_Init(); - // get default video mode - GXRModeObj *vmode = VIDEO_GetPreferredMode(NULL); - - // widescreen fix - VIDEO_Configure (vmode); - - // Allocate the video buffers - xfb = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); - - // A console is always useful while debugging - console_init (xfb, 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); - - // Clear framebuffers etc. - VIDEO_ClearFrameBuffer (vmode, xfb, COLOR_BLACK); - VIDEO_SetNextFramebuffer (xfb); - - VIDEO_SetBlack (FALSE); - VIDEO_Flush (); - VIDEO_WaitVSync (); - if (vmode->viTVMode & VI_NON_INTERLACE) - VIDEO_WaitVSync (); - - //send console output to the gecko - if (geckoinit)CON_EnableGecko(1, true); - textVideoInit = true; - gprintf("...1"); - -} - - -int -main(int argc, char *argv[]) +int main(int argc, char *argv[]) { + MEM2_init(48); + PAD_Init(); + InitVideo(); setlocale(LC_ALL, "en.UTF-8"); geckoinit = InitGecko(); - - if (hbcStubAvailable() || geckoinit) - { - InitTextVideo(); - } - - // DEBUG_Init(GDBSTUB_DEVICE_USB, 1); - //_break(); - - __exception_setreload(5); //auto reset code dump nobody gives us codedump info anyways. - - gprintf("\n\n------------------"); - gprintf("\nUSB Loader GX rev%s",GetRev()); - gprintf("\nmain(%d", argc); - for (int i=0;i"); - gprintf(")"); - - // This part is added, because we need a identify patched ios -// printf("\n\tReloading into ios 236"); - if (IOS_ReloadIOSsafe(236) < 0) - { -// printf("\n\tIOS 236 not found, reloading into 36"); - IOS_ReloadIOSsafe(36); - } + __exception_setreload(5); printf("\n\tStarting up"); - MEM2_init(36); // Initialize 36 MB - MEM2_takeBigOnes(true); - - s32 ret; - bool startupproblem = false; - bool bootDevice_found=false; if (argc >= 1) { @@ -242,144 +90,15 @@ main(int argc, char *argv[]) bootDevice_found = true; } - printf("\n\tInitializing controllers"); - - /** PAD_Init has to be before InitVideo don't move that **/ - PAD_Init(); // initialize PAD/WPAD - - printf("\n\tInitialize USB (wake up)"); - - USBDevice_Init(); // seems enough to wake up some HDDs if they are in sleep mode when the loader starts (tested with WD MyPassport Essential 2.5") - - gprintf("\n\tChecking for stub IOS"); - ios222rev = getIOSrev(0x00000001000000dell); - ios249rev = getIOSrev(0x00000001000000f9ll); - - //if we don't like either of the cIOS then scram - if (!(ios222rev==4 || ios222rev==5 || (ios249rev>=9 && ios249rev<65280))) - { - InitTextVideo(); - printf("\x1b[2J"); - if ((ios222rev < 0 && ios222rev != WII_EINSTALL) && (ios249rev < 0 && ios249rev != WII_EINSTALL)) - { - printf("\n\n\n\tWARNING!"); - printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); - printf("\n\n\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed."); - printf("\n\tTherefor, if loading of USB Loader GX fails, you\n\tprobably have installed the 4.2 update,"); - printf("\n\tand you should go figure out how to get some cios action going on\n\tin your Wii."); - printf("\n\n\tThis message will show every time."); - sleep(5); - } - else - { - printf("\n\n\n\tERROR!"); - printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); - printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65280?" (Stubbed by 4.2 update)":""); - printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65280?" (Stubbed by 4.2 update)":""); - printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); - - sleep(15); - printf("\n\n\tBye"); - - USBDevice_deInit(); - exit(0); - } - } - - printf("\n\tReloading ios 249..."); - ret = IOS_ReloadIOSsafe(249); - - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tIOS 249 failed, reloading ios 222..."); - ret = IOS_ReloadIOSsafe(222); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tIOS 222 failed, reloading ios 250..."); - ret = IOS_ReloadIOSsafe(250); - printf("%d", ret); - - if(ret < 0) - { - printf("\n\tIOS 250 failed, reloading ios 223..."); - ret = IOS_ReloadIOSsafe(223); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tERROR: cIOS could not be loaded!\n"); - sleep(5); - SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); - } - } - } - printf("\n\tInitialize sd card"); - SDCard_Init(); - printf("\n\tLoad ehc module"); - load_ehc_module(); - printf("\n\tdeinit sd card"); - SDCard_deInit(); - } - - printf("\n\tInit wbfs..."); - ret = WBFS_Init(WBFS_DEVICE_USB); - printf("%d", ret); - - if (ret < 0) - { - printf("\n\tYou have issues with a slow disc, or a difficult disc\n\tReloading 222..."); - ret = IOS_ReloadIOSsafe(222); - printf("%d", ret); - /*if(ret < 0) { - // printf("\n\tSleeping for 4 seconds"); - // sleep(4); - - InitVideo(); // Initialise video - Menu_Render(); - BootUpProblems(); - startupproblem = true; - ret = 1; - }*/ - printf("\n\tInitialize sd card"); - SDCard_Init(); - printf("\n\tLoad ehc module"); - load_ehc_module(); - printf("\n\tdeinit sd card"); - SDCard_deInit(); - - printf("\n\tInitialize wbfs..."); - USBDevice_deInit(); - USBDevice_Init(); - ret = WBFS_Init(WBFS_DEVICE_USB); - printf("%d", ret); - - if(ret < 0) - { - // printf("\n\tSleeping for 4 seconds"); - // sleep(4); - InitVideo(); // Initialise video - Menu_Render(); - BootUpProblems(); - startupproblem = true; - ret = 1; - } - } - + //Let's use libogc sd/usb for config loading printf("\n\tInitialize sd card"); - SDCard_Init(); // mount SD for loading cfg's - - //this should have already been done by now in order to WBFS_Init(). + SDCard_Init(); printf("\n\tInitialize usb device"); - USBDevice_Init(); // and mount USB:/ + USBDevice_Init(); if (!bootDevice_found) { printf("\n\tSearch for configuration file"); - //try USB //left in all the dol and elf files in this check in case this is the first time running the app and they dont have the config if (checkfile((char*) "USB:/config/GXglobal.cfg") || (checkfile((char*) "USB:/apps/usbloader_gx/boot.elf")) @@ -390,82 +109,39 @@ main(int argc, char *argv[]) printf("\n\tConfiguration file is on %s", bootDevice); } - // Try opening and closing the configuration file here - // to prevent a crash dump later on - giantpune - char GXGlobal_cfg[26]; - sprintf(GXGlobal_cfg, "%s/config/GXGlobal.cfg", bootDevice); - FILE *fp = fopen(GXGlobal_cfg, "r"); - if (fp) - { - fclose(fp); - } - gettextCleanUp(); printf("\n\tLoading configuration..."); CFG_Load(); printf("done"); - // gprintf("\n\tbootDevice = %s",bootDevice); - /* Load Custom IOS */ - if ((Settings.cios == ios222 && IOS_GetVersion() != 222) || - (Settings.cios == ios223 && IOS_GetVersion() != 223)) + SDCard_deInit();// unmount SD for reloading IOS + USBDevice_deInit();// unmount USB for reloading IOS + USBStorage2_Deinit(); + + // This part is added, because we need a identify patched ios + //! pune please replace this with your magic patch functions - Dimok + if (IOS_ReloadIOSsafe(236) < 0) + IOS_ReloadIOSsafe(36); + + printf("\n\tCheck for an existing cIOS"); + CheckForCIOS(); + + // Let's load the cIOS now + if(LoadAppCIOS() < 0) { - printf("\n\tReloading IOS to config setting (%d)...", Settings.cios == ios222 ? 222 : 223); - SDCard_deInit(); // unmount SD for reloading IOS - USBDevice_deInit(); // unmount USB for reloading IOS - USBStorage2_Deinit(); - ret = IOS_ReloadIOSsafe(Settings.cios == ios222 ? 222 : 223); - printf("%d", ret); - SDCard_Init(); - load_ehc_module(); - if (ret < 0) - { - SDCard_deInit(); - Settings.cios = ios249; - ret = IOS_ReloadIOSsafe(249); - // now mount SD:/ //no need to keep mindlessly mounting and unmounting SD card - SDCard_Init(); - } - - USBDevice_Init(); // and mount USB:/ - WBFS_Init(WBFS_DEVICE_USB); - } else if ((Settings.cios == ios249 && IOS_GetVersion() != 249) || - (Settings.cios == ios250 && IOS_GetVersion() != 250)) - { - - printf("\n\tReloading IOS to config setting (%d)...", ios249 ? 249 : 250); - SDCard_deInit(); // unmount SD for reloading IOS - USBDevice_deInit(); // unmount USB for reloading IOS - USBStorage2_Deinit(); - ret = IOS_ReloadIOSsafe(ios249 ? 249 : 250); - printf("%d", ret); - if (ret < 0) - { - Settings.cios = ios222; - ret = IOS_ReloadIOSsafe(222); - SDCard_Init(); - load_ehc_module(); - } - - else SDCard_Init(); // now mount SD:/ //no need to keep mindlessly mounting and unmounting SD card - USBDevice_Init(); // and mount USB:/ - WBFS_Init(WBFS_DEVICE_USB); - } - - // Partition_GetList(&partitions); - - if (ret < 0) - { - printf("\nERROR: cIOS could not be loaded!"); + printf("\n\tERROR: No cIOS could be loaded. Exiting...."); sleep(5); - exit(0); - //SYS_ResetSystem(SYS_RETURNTOMENU, 0, 0); + Sys_BackToLoader(); } - //gprintf("\n\tcIOS = %u (Rev %u)",IOS_GetVersion(), IOS_GetRevision());//don't need gprintf if sending console shit to gecko, too + printf("\n\tcIOS = %u (Rev %u)",IOS_GetVersion(), IOS_GetRevision()); - // printf("Sleeping for 5 seconds\n"); - // sleep(5); + // Init WBFS + int ret = WBFS_Init(WBFS_DEVICE_USB); + if (ret < 0) + { + printf("\nERROR: No WBFS drive mounted."); + } //if a ID was passed via args copy it and try to boot it after the partition is mounted //its not really a headless mode. more like hairless. @@ -478,9 +154,7 @@ main(int argc, char *argv[]) //! Init the rest of the System Sys_Init(); Wpad_Init(); - if(!startupproblem) - InitVideo(); - InitAudio(); // Initialize audio + InitAudio(); WPAD_SetDataFormat(WPAD_CHAN_ALL,WPAD_FMT_BTNS_ACC_IR); WPAD_SetVRes(WPAD_CHAN_ALL, screenwidth, screenheight); diff --git a/source/memory/mem2.cpp b/source/memory/mem2.cpp index 01630927..7f7361c7 100644 --- a/source/memory/mem2.cpp +++ b/source/memory/mem2.cpp @@ -1,17 +1,27 @@ #include "mem2.h" -#include "mem2alloc.h" +#include "mem2alloc.hpp" #include #include -#define MEM2_PRIORITY_SIZE 0x40 +#define MEM2_PRIORITY_SIZE 2097152 //2MB // Forbid the use of MEM2 through malloc u32 MALLOC_MEM2 = 0; static CMEM2Alloc g_mem2gp; +static bool g_bigGoesToMem2 = false; + +extern "C" +{ + +void MEM2_takeBigOnes(bool b) +{ + g_bigGoesToMem2 = b; +} + void MEM2_init(unsigned int mem2Size) { g_mem2gp.init(mem2Size); @@ -22,41 +32,31 @@ void MEM2_cleanup(void) g_mem2gp.cleanup(); } -extern "C" void *MEM2_alloc(unsigned int s) +void *MEM2_alloc(unsigned int s) { return g_mem2gp.allocate(s); } -extern "C" void MEM2_free(void *p) +void MEM2_free(void *p) { g_mem2gp.release(p); } -extern "C" void *MEM2_realloc(void *p, unsigned int s) +void *MEM2_realloc(void *p, unsigned int s) { return g_mem2gp.reallocate(p, s); } -extern "C" unsigned int MEM2_usableSize(void *p) +unsigned int MEM2_usableSize(void *p) { return CMEM2Alloc::usableSize(p); } -// Give priority to MEM2 for big allocations -// Used for saving some space in malloc, which is required for 2 reasons : -// - decent speed on small and frequent allocations -// - newlib uses its malloc internally (for *printf for example) so it should always have some memory left -bool g_bigGoesToMem2 = false; - -void MEM2_takeBigOnes(bool b) +unsigned int MEM2_freesize() { - g_bigGoesToMem2 = b; + return g_mem2gp.FreeSize(); } - -extern "C" -{ - extern __typeof(malloc) __real_malloc; extern __typeof(calloc) __real_calloc; extern __typeof(realloc) __real_realloc; @@ -133,9 +133,12 @@ void __wrap_free(void *p) if(!p) return; - if (((u32)p & 0x10000000) != 0) { + if (((u32)p & 0x10000000) != 0) + { MEM2_free(p); - } else { + } + else + { __real_free(p); } } @@ -185,4 +188,4 @@ size_t __wrap_malloc_usable_size(void *p) return __real_malloc_usable_size(p); } -} +} ///extern "C" diff --git a/source/memory/mem2.h b/source/memory/mem2.h index 410705f6..b8659631 100644 --- a/source/memory/mem2.h +++ b/source/memory/mem2.h @@ -1,26 +1,24 @@ -// 2 MEM2 allocators, one for general purpose, one for covers -// Aligned and padded to 32 bytes, as required by many functions - -#ifndef __MEM2_HPP -#define __MEM2_HPP - -#ifdef __cplusplus -extern "C" -{ -#endif - -void *MEM2_alloc(unsigned int s); -void *MEM2_realloc(void *p, unsigned int s); -void MEM2_free(void *p); -unsigned int MEM2_usableSize(void *p); - -#ifdef __cplusplus -} - -void MEM2_init(unsigned int mem2Size); -void MEM2_cleanup(void); -void MEM2_takeBigOnes(bool b); - -#endif - -#endif // !defined(__MEM2_HPP) +// 2 MEM2 allocators, one for general purpose, one for covers +// Aligned and padded to 32 bytes, as required by many functions + +#ifndef __MEM2_H_ +#define __MEM2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void MEM2_init(unsigned int mem2Size); +void MEM2_cleanup(void); +void MEM2_takeBigOnes(bool b); +void *MEM2_alloc(unsigned int s); +void *MEM2_realloc(void *p, unsigned int s); +void MEM2_free(void *p); +unsigned int MEM2_usableSize(void *p); +unsigned int MEM2_freesize(); + +#ifdef __cplusplus +} +#endif + +#endif // !defined(__MEM2_H_) diff --git a/source/memory/mem2alloc.cpp b/source/memory/mem2alloc.cpp index c6898b4a..50209ee2 100644 --- a/source/memory/mem2alloc.cpp +++ b/source/memory/mem2alloc.cpp @@ -1,201 +1,238 @@ - -#include "mem2alloc.h" - -#include -#include -#include - - -class LockMutex -{ - mutex_t &m_mutex; -public: - LockMutex(mutex_t &m) : m_mutex(m) { LWP_MutexLock(m_mutex); } - ~LockMutex(void) { LWP_MutexUnlock(m_mutex); } -}; - -void CMEM2Alloc::init(unsigned int size) -{ - m_baseAddress = (SBlock *)(((u32)SYS_GetArena2Lo() + 31) & ~31); - m_endAddress = (SBlock *)((char *)m_baseAddress + std::min(size * 0x100000, SYS_GetArena2Size() & ~31)); - if (m_endAddress > (SBlock *)0x93000000) // See loader/disc.c - m_endAddress = (SBlock *)0x93000000; - SYS_SetArena2Lo(m_endAddress); - LWP_MutexInit(&m_mutex, 0); -} - -void CMEM2Alloc::init(void *addr, void *end) -{ - m_baseAddress = (SBlock *)(((u32)addr + 31) & ~31); - m_endAddress = (SBlock *)((u32)end & ~31); - LWP_MutexInit(&m_mutex, 0); -} - -void CMEM2Alloc::cleanup(void) -{ - LWP_MutexDestroy(m_mutex); - m_mutex = 0; - m_first = 0; -// // Try to release the range we took through SYS functions -// if (SYS_GetArena2Lo() == m_endAdress) -// SYS_SetArena2Lo(m_baseAddress); - m_baseAddress = 0; - m_endAddress = 0; -} - -void CMEM2Alloc::clear(void) -{ - m_first = 0; - memset(m_baseAddress, 0, (u8 *)m_endAddress - (u8 *)m_endAddress); -} - -unsigned int CMEM2Alloc::usableSize(void *p) -{ - return p == 0 ? 0 : ((SBlock *)p - 1)->s * sizeof (SBlock); -} - -void *CMEM2Alloc::allocate(unsigned int s) -{ - if (s == 0) - s = 1; - // - LockMutex lock(m_mutex); - // - s = (s - 1) / sizeof (SBlock) + 1; - // First block - if (m_first == 0) - { - if (m_baseAddress + s + 1 >= m_endAddress) - return 0; - m_first = m_baseAddress; - m_first->next = 0; - m_first->prev = 0; - m_first->s = s; - m_first->f = false; - return (void *)(m_first + 1); - } - // Search for a free block - SBlock *i; - SBlock *j; - for (i = m_first; i != 0; i = i->next) - { - if (i->f && i->s >= s) - break; - j = i; - } - // Create a new block - if (i == 0) - { - i = j + j->s + 1; - if (i + s + 1 >= m_endAddress) - return 0; - j->next = i; - i->prev = j; - i->next = 0; - i->s = s; - i->f = false; - return (void *)(i + 1); - } - // Reuse a free block - i->f = false; - // Split it - if (i->s > s + 1) - { - j = i + s + 1; - j->f = true; - j->s = i->s - s - 1; - i->s = s; - j->next = i->next; - j->prev = i; - i->next = j; - if (j->next != 0) - j->next->prev = j; - } - return (void *)(i + 1); -} - -void CMEM2Alloc::release(void *p) -{ - if (p == 0) - return; - LockMutex lock(m_mutex); - SBlock *i = (SBlock *)p - 1; - i->f = true; - // Merge with previous block - if (i->prev != 0 && i->prev->f) - { - i = i->prev; - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } - // Merge with next block - if (i->next != 0 && i->next->f) - { - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } -} - -void *CMEM2Alloc::reallocate(void *p, unsigned int s) -{ - SBlock *i; - SBlock *j; - void *n; - - if (s == 0) - s = 1; - if (p == 0) - return allocate(s); - i = (SBlock *)p - 1; - s = (s - 1) / sizeof (SBlock) + 1; - { - LockMutex lock(m_mutex); - if (i + s + 1 >= m_endAddress) - return 0; - - // Last block - if (i->next == 0 && i + s + 1 < m_endAddress) - { - i->s = s; - return p; - } - // Size <= current size + next block - if (i->s < s && i->next->f && i->s + i->next->s + 1 >= s) - { - // Merge - i->s += i->next->s + 1; - i->next = i->next->next; - if (i->next != 0) - i->next->prev = i; - } - // Size <= current size - if (i->s >= s) - { - // Split - if (i->s > s + 1) - { - j = i + s + 1; - j->f = true; - j->s = i->s - s - 1; - i->s = s; - j->next = i->next; - j->prev = i; - i->next = j; - if (j->next != 0) - j->next->prev = j; - } - return p; - } - } - // Size > current size - n = allocate(s * sizeof (SBlock)); - if (n == 0) - return 0; - memcpy(n, p, i->s * sizeof (SBlock)); - release(p); - return n; -} + +#include "mem2alloc.hpp" + +#include +#include +#include + + +class LockMutex +{ + mutex_t &m_mutex; +public: + LockMutex(mutex_t &m) : m_mutex(m) { LWP_MutexLock(m_mutex); } + ~LockMutex(void) { LWP_MutexUnlock(m_mutex); } +}; + +void CMEM2Alloc::init(unsigned int size) +{ + m_baseAddress = (SBlock *) (((u32)SYS_GetArena2Lo() + 31) & ~31); + m_endAddress = (SBlock *) ((char *)m_baseAddress + std::min(size * 0x100000, SYS_GetArena2Size() & ~31)); + if (m_endAddress > (SBlock *) 0x93300000) //rest is reserved for usb/usb2/network and other stuff... (0xE0000 bytes) + m_endAddress = (SBlock *) 0x93300000; + SYS_SetArena2Lo(m_endAddress); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::init(void *addr, void *end) +{ + m_baseAddress = (SBlock *)(((u32)addr + 31) & ~31); + m_endAddress = (SBlock *)((u32)end & ~31); + LWP_MutexInit(&m_mutex, 0); +} + +void CMEM2Alloc::cleanup(void) +{ + LWP_MutexDestroy(m_mutex); + m_mutex = 0; + m_first = 0; + // Try to release the range we took through SYS functions + if (SYS_GetArena2Lo() == m_endAddress) + SYS_SetArena2Lo(m_baseAddress); + m_baseAddress = 0; + m_endAddress = 0; +} + +void CMEM2Alloc::clear(void) +{ + m_first = 0; + memset(m_baseAddress, 0, (u8 *)m_endAddress - (u8 *)m_endAddress); +} + +unsigned int CMEM2Alloc::usableSize(void *p) +{ + return p == 0 ? 0 : ((SBlock *)p - 1)->s * sizeof (SBlock); +} + +void *CMEM2Alloc::allocate(unsigned int s) +{ + if (s == 0) + s = 1; + // + LockMutex lock(m_mutex); + // + s = (s - 1) / sizeof (SBlock) + 1; + // First block + if (m_first == 0) + { + if (m_baseAddress + s + 1 >= m_endAddress) + return 0; + m_first = m_baseAddress; + m_first->next = 0; + m_first->prev = 0; + m_first->s = s; + m_first->f = false; + return (void *)(m_first + 1); + } + // Search for a free block + SBlock *i; + SBlock *j; + for (i = m_first; i != 0; i = i->next) + { + if (i->f && i->s >= s) + break; + j = i; + } + // Create a new block + if (i == 0) + { + i = j + j->s + 1; + if (i + s + 1 >= m_endAddress) + return 0; + j->next = i; + i->prev = j; + i->next = 0; + i->s = s; + i->f = false; + return (void *)(i + 1); + } + // Reuse a free block + i->f = false; + // Split it + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return (void *)(i + 1); +} + +void CMEM2Alloc::release(void *p) +{ + if (p == 0) + return; + + LockMutex lock(m_mutex); + SBlock *i = (SBlock *)p - 1; + i->f = true; + + // If there are no other blocks following yet, + // set the remaining size to free size. - Dimok + if(i->next == 0) + i->s = m_endAddress - i - 1; + + // Merge with previous block + if (i->prev != 0 && i->prev->f) + { + i = i->prev; + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Merge with next block + if (i->next != 0 && i->next->f) + { + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } +} + +void *CMEM2Alloc::reallocate(void *p, unsigned int s) +{ + SBlock *i; + SBlock *j; + void *n; + + if (s == 0) + s = 1; + if (p == 0) + return allocate(s); + + i = (SBlock *)p - 1; + s = (s - 1) / sizeof (SBlock) + 1; + { + LockMutex lock(m_mutex); + + //out of memory /* Dimok */ + if (i + s + 1 >= m_endAddress) + { + return 0; + } + + // Last block + if (i->next == 0 && i + s + 1 < m_endAddress) + { + i->s = s; + return p; + } + // Size <= current size + next block + if (i->next != 0 && i->s < s && i->next->f && i->s + i->next->s + 1 >= s) + { + // Merge + i->s += i->next->s + 1; + i->next = i->next->next; + if (i->next != 0) + i->next->prev = i; + } + // Size <= current size + if (i->s >= s) + { + // Split + if (i->s > s + 1) + { + j = i + s + 1; + j->f = true; + j->s = i->s - s - 1; + i->s = s; + j->next = i->next; + j->prev = i; + i->next = j; + if (j->next != 0) + j->next->prev = j; + } + return p; + } + } + // Size > current size + n = allocate(s * sizeof (SBlock)); + if (n == 0) + return 0; + memcpy(n, p, i->s * sizeof (SBlock)); + release(p); + return n; +} + +unsigned int CMEM2Alloc::FreeSize() +{ + LockMutex lock(m_mutex); + + if (m_first == 0) + return (const char *) m_endAddress - (const char *) m_baseAddress; + + SBlock *i; + unsigned int size = 0; + + for(i = m_first; i != 0; i = i->next) + { + if(i->f && i->next != 0) + size += i->s; + + else if(i->f && i->next == 0) + size += m_endAddress - i - 1; + + else if(!i->f && i->next == 0) + size += m_endAddress - i - i->s - 1; + } + + return size*sizeof(SBlock); +} diff --git a/source/memory/mem2alloc.h b/source/memory/mem2alloc.hpp similarity index 95% rename from source/memory/mem2alloc.h rename to source/memory/mem2alloc.hpp index 6d681b09..e66f524a 100644 --- a/source/memory/mem2alloc.h +++ b/source/memory/mem2alloc.hpp @@ -1,41 +1,42 @@ -// MEM2 allocator -// Made as a class so i can have 2 sections, one being dedicated to the covers - -#ifndef __MEM2ALLOC_HPP -#define __MEM2ALLOC_HPP - -#include - -class CMEM2Alloc -{ -public: - void *allocate(unsigned int s); - void release(void *p); - void *reallocate(void *p, unsigned int s); - void init(unsigned int size); - void init(void *addr, void *end); - void cleanup(void); - void clear(void); - static unsigned int usableSize(void *p); - void forceEndAddress(void *newAddr) { m_endAddress = (SBlock *)newAddr; } - void *getEndAddress(void) const { return m_endAddress; } - void info(void *&address, unsigned int &size) const { address = m_baseAddress; size = (const char *)m_endAddress - (const char *)m_baseAddress; } - // - CMEM2Alloc(void) : m_baseAddress(0), m_endAddress(0), m_first(0), m_mutex(0) { } -private: - struct SBlock - { - unsigned int s; - SBlock *next; - SBlock *prev; - bool f; - } __attribute__((aligned(32))); - SBlock *m_baseAddress; - SBlock *m_endAddress; - SBlock *m_first; - mutex_t m_mutex; -private: - CMEM2Alloc(const CMEM2Alloc &); -}; - -#endif // !defined(__MEM2ALLOC_HPP) +// MEM2 allocator +// Made as a class so i can have 2 sections, one being dedicated to the covers + +#ifndef __MEM2ALLOC_HPP +#define __MEM2ALLOC_HPP + +#include + +class CMEM2Alloc +{ +public: + void *allocate(unsigned int s); + void release(void *p); + void *reallocate(void *p, unsigned int s); + void init(unsigned int size); + void init(void *addr, void *end); + void cleanup(void); + void clear(void); + static unsigned int usableSize(void *p); + void forceEndAddress(void *newAddr) { m_endAddress = (SBlock *)newAddr; } + void *getEndAddress(void) const { return m_endAddress; } + void info(void *&address, unsigned int &size) const { address = m_baseAddress; size = (const char *)m_endAddress - (const char *)m_baseAddress; } + unsigned int FreeSize(); + // + CMEM2Alloc(void) : m_baseAddress(0), m_endAddress(0), m_first(0), m_mutex(0) { } +private: + struct SBlock + { + unsigned int s; + SBlock *next; + SBlock *prev; + bool f; + } __attribute__((aligned(32))); + SBlock *m_baseAddress; + SBlock *m_endAddress; + SBlock *m_first; + mutex_t m_mutex; +private: + CMEM2Alloc(const CMEM2Alloc &); +}; + +#endif // !defined(__MEM2ALLOC_HPP) diff --git a/source/menu.cpp b/source/menu.cpp index ee849e29..242b2b53 100644 --- a/source/menu.cpp +++ b/source/menu.cpp @@ -350,10 +350,8 @@ int MainMenu(int menu) { gettextCleanUp(); if(dbvideo) - { - InitVideodebug (); - //printf("\n\n\n\n\n"); - } + InitVideodebug (); + if (mountMethod==3) { struct discHdr *header = &gameList[gameSelected]; @@ -372,13 +370,13 @@ int MainMenu(int menu) { WII_LaunchTitle(0x0000000100000100ULL); } - else if (boothomebrew == 1) { + if (boothomebrew == 1) { gprintf("\nBootHomebrew"); BootHomebrew(Settings.selected_homebrew); } else if (boothomebrew == 2) { - gprintf("\nBootHomebrewFromMenu"); - BootHomebrewFromMem(); + gprintf("\nBootHomebrew from Menu"); + BootHomebrew(); } else { gprintf("\n\tSettings.partition:%d",Settings.partition); diff --git a/source/menu/menu_disclist.cpp b/source/menu/menu_disclist.cpp index 563c5af7..1cdecf26 100644 --- a/source/menu/menu_disclist.cpp +++ b/source/menu/menu_disclist.cpp @@ -577,7 +577,7 @@ int MenuDiscList() { w.Append(&sdcardBtn); w.Append(&poweroffBtn); w.Append(&gameInfo); - if (Settings.godmode && load_from_fs != PART_FS_NTFS) + if (Settings.godmode) w.Append(&installBtn); w.Append(&homeBtn); w.Append(&settingsBtn); @@ -800,18 +800,10 @@ int MenuDiscList() { gameCarousel->SetFocus(1); } } else if (installBtn.GetState() == STATE_CLICKED) { - gprintf("\n\tinstallBtn clicked"); - if (load_from_fs == PART_FS_NTFS) - { - choice = 0; - WindowPrompt(tr("Install not possible"), tr("You are using NTFS filesystem. Due to possible write errors to a NTFS partition, installing a game is not possible."), tr("OK")); - } - else - choice = WindowPrompt(tr("Install a game"),0,tr("Yes"),tr("No")); + choice = WindowPrompt(tr("Install a game"),0,tr("Yes"),tr("No")); if (choice == 1) { - if (load_from_fs == PART_FS_NTFS) - menu = MENU_INSTALL; + menu = MENU_INSTALL; break; } else @@ -825,7 +817,7 @@ int MenuDiscList() { gameCarousel->SetFocus(1); } } - } else if ((covert & 0x2)&&(covert!=covertOld) && load_from_fs != PART_FS_NTFS) { + } else if ((covert & 0x2)&&(covert!=covertOld)) { gprintf("\n\tNew Disc Detected"); choice = WindowPrompt(tr("New Disc Detected"),0,tr("Install"),tr("Mount DVD drive"),tr("Cancel")); if (choice == 1) { diff --git a/source/sys.cpp b/source/sys.cpp index 68abdfc8..7ddfd742 100644 --- a/source/sys.cpp +++ b/source/sys.cpp @@ -12,8 +12,10 @@ #include "audio.h" #include "menu.h" #include "fatmounter.h" +#include "gecko.h" #include "sys.h" #include "wpad.h" +#include "lstub.h" extern char game_partition[6]; extern u8 load_from_fs; @@ -197,7 +199,8 @@ void Sys_LoadMenu(void) { } void Sys_BackToLoader(void) { - if (*((u32*) 0x80001800)) { + + if (hbcStubAvailable()) { _ExitApp(); exit(0); } @@ -264,6 +267,85 @@ s32 IOS_ReloadIOSsafe(int ios) return r; } +s32 CheckForCIOS() +{ + gprintf("\n\tChecking for stub IOS"); + s32 ret = 1; + ios222rev = getIOSrev(0x00000001000000dell); + ios249rev = getIOSrev(0x00000001000000f9ll); + + //if we don't like either of the cIOS then scram + if (!((ios222rev >= 4 && ios222rev < 65280) || (ios249rev >=9 && ios249rev < 65280))) + { + printf("\x1b[2J"); + if ((ios222rev < 0 && ios222rev != WII_EINSTALL) && (ios249rev < 0 && ios249rev != WII_EINSTALL)) { + printf("\n\n\n\tWARNING!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tWe cannot determine the versions on your system,\n\tsince you have no patched ios 36 or 236 installed."); + printf("\n\tTherefor, if loading of USB Loader GX fails, you\n\tprobably have installed the 4.2 update,"); + printf("\n\tand you should go figure out how to get some cios action going on\n\tin your Wii."); + printf("\n\n\tThis message will show every time."); + sleep(5); + } else { + printf("\n\n\n\tERROR!"); + printf("\n\tUSB Loader GX needs unstubbed cIOS 222 v4 or 249 v9+"); + printf("\n\n\tI found \n\t\t222 = %d%s",ios222rev,ios222rev==65280?" (Stubbed by 4.2 update)":""); + printf("\n\t\t249 = %d%s",ios249rev,ios249rev==65280?" (Stubbed by 4.2 update)":""); + printf("\n\n\tGo figure out how to get some cIOS action going on\n\tin your Wii and come back and see me."); + + sleep(15); + printf("\n\n\tBye"); + + USBDevice_deInit(); + exit(0); + } + } + + return ret; +} + +int LoadAppCIOS() +{ + s32 ret = 1; + /* Load Custom IOS */ + SDCard_deInit();// unmount SD for reloading IOS + USBDevice_deInit();// unmount USB for reloading IOS + USBStorage2_Deinit(); + + if (Settings.cios == ios222 && IOS_GetVersion() != 222) + { + printf("\n\tReloading IOS to config setting (222)..."); + ret = IOS_ReloadIOSsafe(222); + printf("%d", ret); + if (ret < 0) + { + Settings.cios = ios249; + IOS_ReloadIOSsafe(249); + } + } + + if ((Settings.cios == ios249 && IOS_GetVersion() != 249) + || (Settings.cios == ios250 && IOS_GetVersion() != 250)) + { + printf("\n\tReloading IOS to config setting (%d)...", (Settings.cios == ios249) ? 249 : 250); + ret = IOS_ReloadIOSsafe((Settings.cios == ios249) ? 249 : 250); + printf("%d", ret); + if (ret < 0) { + Settings.cios = ios222; + ret = IOS_ReloadIOSsafe(222); + } + } + + SDCard_Init(); + if(IOS_GetVersion() == 222) + load_ehc_module(); + + USBDevice_Init(); + + return ret; +} + + #include void ScreenShot() diff --git a/source/sys.h b/source/sys.h index 2ee89d1b..9fcd55ec 100644 --- a/source/sys.h +++ b/source/sys.h @@ -16,7 +16,8 @@ int Sys_IosReload(int IOS); bool Sys_IsHermes(); s32 IOS_ReloadIOSsafe(int ios); void ScreenShot(); - +s32 CheckForCIOS(); +int LoadAppCIOS(); void ShowMemInfo(); extern s32 ios222rev; extern s32 ios223rev; diff --git a/source/video.cpp b/source/video.cpp index c82226cc..2d9f2a50 100644 --- a/source/video.cpp +++ b/source/video.cpp @@ -177,9 +177,6 @@ InitVideo () { xfb[0] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); xfb[1] = (u32 *) MEM_K0_TO_K1 (SYS_AllocateFramebuffer (vmode)); - // A console is always useful while debugging - console_init (xfb[0], 20, 64, vmode->fbWidth, vmode->xfbHeight, vmode->fbWidth * 2); - // Clear framebuffers etc. VIDEO_ClearFrameBuffer (vmode, xfb[0], COLOR_BLACK); VIDEO_ClearFrameBuffer (vmode, xfb[1], COLOR_BLACK); @@ -197,6 +194,9 @@ InitVideo () { StartGX(); ResetVideo_Menu(); // Finally, the video is up and ready for use :) + + // A console is always useful while debugging + console_init (xfb[0], 80, 100, 500, 350, vmode->fbWidth * 2); } static unsigned int *xfbDB = NULL;