diff --git a/libcustomfat/cache.c b/libcustomfat/cache.c index 164d3408..07576b9c 100644 --- a/libcustomfat/cache.c +++ b/libcustomfat/cache.c @@ -46,7 +46,7 @@ #define CACHE_FREE UINT_MAX -CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition) { +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector) { CACHE* cache; unsigned int i; CACHE_ENTRY* cacheEntries; @@ -68,6 +68,7 @@ CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsP cache->endOfPartition = endOfPartition; cache->numberOfPages = numberOfPages; cache->sectorsPerPage = sectorsPerPage; + cache->bytesPerSector = bytesPerSector; cacheEntries = (CACHE_ENTRY*) _FAT_mem_allocate ( sizeof(CACHE_ENTRY) * numberOfPages); @@ -81,7 +82,7 @@ CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsP cacheEntries[i].count = 0; cacheEntries[i].last_access = 0; cacheEntries[i].dirty = false; - cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * BYTES_PER_READ ); + cacheEntries[i].cache = (uint8_t*) _FAT_mem_align ( sectorsPerPage * bytesPerSector ); } cache->cacheEntries = cacheEntries; @@ -127,7 +128,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_accesscount - sec; if(secs_to_read>numSectors) secs_to_read = numSectors; - memcpy(dest,entry->cache + (sec*BYTES_PER_READ),(secs_to_read*BYTES_PER_READ)); + memcpy(dest,entry->cache + (sec*cache->bytesPerSector),(secs_to_read*cache->bytesPerSector)); - dest += (secs_to_read*BYTES_PER_READ); + dest += (secs_to_read*cache->bytesPerSector); sector += secs_to_read; numSectors -= secs_to_read; } @@ -181,18 +182,18 @@ bool _FAT_cache_readSectors(CACHE *cache,sec_t sector,sec_t numSectors,void *buf /* Reads some data from a cache page, determined by the sector number */ -bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) +bool _FAT_cache_readPartialSector (CACHE* cache, void* buffer, sec_t sector, unsigned int offset, size_t size) { sec_t sec; CACHE_ENTRY *entry; - if (offset + size > BYTES_PER_READ) return false; + if (offset + size > cache->bytesPerSector) return false; entry = _FAT_cache_getPage(cache,sector); if(entry==NULL) return false; sec = sector - entry->sector; - memcpy(buffer,entry->cache + ((sec*BYTES_PER_READ) + offset),size); + memcpy(buffer,entry->cache + ((sec*cache->bytesPerSector) + offset),size); return true; } @@ -213,18 +214,18 @@ bool _FAT_cache_readLittleEndianValue (CACHE* cache, uint32_t *value, sec_t sect /* Writes some data to a cache page, making sure it is loaded into memory first. */ -bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +bool _FAT_cache_writePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) { sec_t sec; CACHE_ENTRY *entry; - if (offset + size > BYTES_PER_READ) return false; + if (offset + size > cache->bytesPerSector) return false; entry = _FAT_cache_getPage(cache,sector); if(entry==NULL) return false; sec = sector - entry->sector; - memcpy(entry->cache + ((sec*BYTES_PER_READ) + offset),buffer,size); + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); entry->dirty = true; return true; @@ -246,26 +247,26 @@ bool _FAT_cache_writeLittleEndianValue (CACHE* cache, const uint32_t value, sec_ /* Writes some data to a cache page, zeroing out the page first */ -bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) +bool _FAT_cache_eraseWritePartialSector (CACHE* cache, const void* buffer, sec_t sector, unsigned int offset, size_t size) { sec_t sec; CACHE_ENTRY *entry; - if (offset + size > BYTES_PER_READ) return false; + if (offset + size > cache->bytesPerSector) return false; entry = _FAT_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); + memset(entry->cache + (sec*cache->bytesPerSector),0,cache->bytesPerSector); + memcpy(entry->cache + ((sec*cache->bytesPerSector) + offset),buffer,size); entry->dirty = true; return true; } -bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) +bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer) { sec_t sec; sec_t secs_to_write; @@ -281,9 +282,9 @@ bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, cons 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)); + memcpy(entry->cache + (sec*cache->bytesPerSector),src,(secs_to_write*cache->bytesPerSector)); - src += (secs_to_write*BYTES_PER_READ); + src += (secs_to_write*cache->bytesPerSector); sector += secs_to_write; numSectors -= secs_to_write; diff --git a/libcustomfat/cache.h b/libcustomfat/cache.h index c6e48d07..07bb14c2 100644 --- a/libcustomfat/cache.h +++ b/libcustomfat/cache.h @@ -39,9 +39,6 @@ #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; @@ -55,6 +52,7 @@ typedef struct { sec_t endOfPartition; unsigned int numberOfPages; unsigned int sectorsPerPage; + unsigned int bytesPerSector; CACHE_ENTRY* cacheEntries; } CACHE; @@ -100,14 +98,14 @@ bool _FAT_cache_readSectors (CACHE* cache, sec_t sector, sec_t numSectors, void* 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); + return _FAT_cache_readPartialSector (cache, buffer, sector, 0, cache->bytesPerSector); } /* 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); + return _FAT_cache_writePartialSector (cache, buffer, sector, 0, cache->bytesPerSector); } bool _FAT_cache_writeSectors (CACHE* cache, sec_t sector, sec_t numSectors, const void* buffer); @@ -122,7 +120,7 @@ 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); +CACHE* _FAT_cache_constructor (unsigned int numberOfPages, unsigned int sectorsPerPage, const DISC_INTERFACE* discInterface, sec_t endOfPartition, unsigned int bytesPerSector); void _FAT_cache_destructor (CACHE* cache); diff --git a/libcustomfat/common.h b/libcustomfat/common.h index b3358f0c..c5c56325 100644 --- a/libcustomfat/common.h +++ b/libcustomfat/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: @@ -29,7 +29,6 @@ #ifndef _COMMON_H #define _COMMON_H -#define BYTES_PER_READ 512 #include #include #include diff --git a/libcustomfat/directory.c b/libcustomfat/directory.c index c2d93131..55de3bb9 100644 --- a/libcustomfat/directory.c +++ b/libcustomfat/directory.c @@ -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: @@ -53,7 +53,7 @@ typedef unsigned short ucs2_t; // Long file name directory entry enum LFN_offset { LFN_offset_ordinal = 0x00, // Position within LFN - LFN_offset_char0 = 0x01, + LFN_offset_char0 = 0x01, LFN_offset_char1 = 0x03, LFN_offset_char2 = 0x05, LFN_offset_char3 = 0x07, @@ -71,7 +71,7 @@ enum LFN_offset { LFN_offset_char11 = 0x1C, LFN_offset_char12 = 0x1E }; -static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; +static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E}; #define LFN_END 0x40 #define LFN_DEL 0x80 @@ -79,7 +79,7 @@ static const int LFN_offset_table[13]={0x01,0x03,0x05,0x07,0x09,0x0E,0x10,0x12,0 static const char ILLEGAL_ALIAS_CHARACTERS[] = "\\/:;*?\"<>|&+,=[] "; static const char ILLEGAL_LFN_CHARACTERS[] = "\\/:*?\"<>|"; -/* +/* Returns number of UCS-2 characters needed to encode an LFN Returns -1 if it is an invalid LFN */ @@ -89,7 +89,7 @@ static int _FAT_directory_lfnLength (const char* name) { size_t nameLength; int ucsLength; const char* tempName = name; - + nameLength = strnlen(name, MAX_FILENAME_LENGTH); // Make sure the name is short enough to be valid if ( nameLength >= MAX_FILENAME_LENGTH) { @@ -110,7 +110,7 @@ static int _FAT_directory_lfnLength (const char* name) { if (ucsLength < 0 || ucsLength >= MAX_LFN_LENGTH) { return -1; } - + // Otherwise it is valid return ucsLength; } @@ -153,7 +153,7 @@ static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len int bytes; char buff[MB_CUR_MAX]; int i; - + while (count < len - 1 && *src != '\0') { bytes = wcrtomb (buff, *src, &ps); if (bytes < 0) { @@ -170,7 +170,7 @@ static size_t _FAT_directory_ucs2tombs (char* dst, const ucs2_t* src, size_t len } } *dst = L'\0'; - + return count; } @@ -183,11 +183,11 @@ static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t le mbstate_t ps2 = {0}; size_t b1 = 0; size_t b2 = 0; - + if (len1 == 0) { return 0; } - + do { s1 += b1; s2 += b2; @@ -198,7 +198,7 @@ static int _FAT_directory_mbsncasecmp (const char* s1, const char* s2, size_t le } len1 -= b1; } while (len1 > 0 && towlower(wc1) == towlower(wc2) && wc1 != 0); - + return towlower(wc1) - towlower(wc2); } @@ -217,7 +217,7 @@ static bool _FAT_directory_entryGetAlias (const u8* entryData, char* destName) { } else { destName[1] = '\0'; } - } else { + } else { // Copy the filename from the dirEntry to the string for (i = 0; (i < 8) && (entryData[DIR_ENTRY_name + i] != ' '); i++) { destName[i] = entryData[DIR_ENTRY_name + i]; @@ -251,7 +251,7 @@ static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ // Increment offset, wrapping at the end of a sector ++ position.offset; - if (position.offset == BYTES_PER_READ / DIR_ENTRY_DATA_SIZE) { + if (position.offset == partition->bytesPerSector / DIR_ENTRY_DATA_SIZE) { position.offset = 0; // Increment sector when wrapping ++ position.sector; @@ -269,7 +269,7 @@ static bool _FAT_directory_incrementDirEntryPosition (PARTITION* partition, DIR_ } else { return false; // Got to the end of the directory, not extending it } - } + } position.cluster = tempCluster; } else if ((position.cluster == FAT16_ROOT_DIR_CLUSTER) && (position.sector == (partition->dataStart - partition->rootDirStart))) { return false; // Got to end of root directory, can't extend it @@ -329,7 +329,7 @@ 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]) { lfnExists = false; } @@ -353,14 +353,14 @@ bool _FAT_directory_getNextEntry (PARTITION* partition, DIR_ENTRY* entry) { chkSum = 0; for (i=0; i < 11; i++) { // NOTE: The operation is an unsigned char rotate right - chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; + chkSum = ((chkSum & 1) ? 0x80 : 0) + (chkSum >> 1) + entryData[i]; } if (chkSum != lfnChkSum) { lfnExists = false; entry->filename[0] = '\0'; } } - + if (lfnExists) { if (_FAT_directory_ucs2tombs (entry->filename, lfn, MAX_FILENAME_LENGTH) == (size_t)-1) { // Failed to convert the file name to UTF-8. Maybe the wrong locale is set? @@ -400,21 +400,21 @@ bool _FAT_directory_getRootEntry (PARTITION* partition, DIR_ENTRY* entry) { entry->dataStart.cluster = 0; entry->dataStart.sector = 0; entry->dataStart.offset = 0; - - entry->dataEnd = entry->dataStart; - + + entry->dataEnd = entry->dataStart; + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); entry->filename[0] = '.'; - + memset (entry->entryData, 0, DIR_ENTRY_DATA_SIZE); memset (entry->entryData, ' ', 11); entry->entryData[0] = '.'; - + entry->entryData[DIR_ENTRY_attributes] = ATTRIB_DIR; - + u16_to_u8array (entry->entryData, DIR_ENTRY_cluster, partition->rootDirCluster); u16_to_u8array (entry->entryData, DIR_ENTRY_clusterHigh, partition->rootDirCluster >> 16); - + return true; } @@ -438,7 +438,7 @@ bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { 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) { + while(!end) { 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)) @@ -446,15 +446,15 @@ bool _FAT_directory_getVolumeLabel (PARTITION* partition, char *label) { return false; } - if (entryData[DIR_ENTRY_attributes] == ATTRIB_VOL && entryData[0] != DIR_ENTRY_FREE) { + 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]; + label[i] = entryData[DIR_ENTRY_name + i]; } return true; } else if (entryData[0] == DIR_ENTRY_LAST) { end = true; } - + if (_FAT_directory_incrementDirEntryPosition (partition, &entryEnd, false) == false) { end = true; } @@ -471,18 +471,18 @@ bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { int i; int lfnPos; uint8_t entryData[DIR_ENTRY_DATA_SIZE]; - + memset (entry->filename, '\0', MAX_FILENAME_LENGTH); // Create an empty directory entry to overwrite the old ones with - for ( entryStillValid = true, finished = false; - entryStillValid && !finished; + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) { - _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); - + if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { @@ -504,7 +504,7 @@ bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { if (!entryStillValid) { return false; } - + if ((entryStart.cluster == entryEnd.cluster) && (entryStart.sector == entryEnd.sector) && (entryStart.offset == entryEnd.offset)) { @@ -518,7 +518,7 @@ bool _FAT_directory_entryFromPosition (PARTITION* partition, DIR_ENTRY* entry) { return false; } } - + return true; } @@ -534,7 +534,7 @@ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const bool found, notFound; pathPosition = path; - + found = false; notFound = false; @@ -560,13 +560,13 @@ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const dirCluster = partition->cwdCluster; } - // If the path is only specifying a directory in the form "." + // If the path is only specifying a directory in the form "." // and this is the root directory, return it if ((dirCluster == partition->rootDirCluster) && (strcmp(".", pathPosition) == 0)) { _FAT_directory_getRootEntry (partition, entry); found = true; } - + while (!found && !notFound) { // Get the name of the next required subdirectory within the path nextPathPosition = strchr (pathPosition, DIR_SEPARATOR); @@ -633,7 +633,7 @@ bool _FAT_directory_entryFromPath (PARTITION* partition, DIR_ENTRY* entry, const if (found && !notFound) { if (partition->filesysType == FS_FAT32 && (entry->entryData[DIR_ENTRY_attributes] & ATTRIB_DIR) && - _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) + _FAT_directory_entryGetCluster (partition, entry->entryData) == CLUSTER_ROOT) { // On FAT32 it should specify an actual cluster for the root entry, // not cluster 0 as on FAT16 @@ -653,8 +653,8 @@ bool _FAT_directory_removeEntry (PARTITION* partition, DIR_ENTRY* entry) { uint8_t entryData[DIR_ENTRY_DATA_SIZE]; // Create an empty directory entry to overwrite the old ones with - for ( entryStillValid = true, finished = false; - entryStillValid && !finished; + for ( entryStillValid = true, finished = false; + entryStillValid && !finished; entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &entryStart, false)) { _FAT_cache_readPartialSector (partition->cache, entryData, _FAT_fat_clusterToSector(partition, entryStart.cluster) + entryStart.sector, entryStart.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); @@ -689,10 +689,10 @@ static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, entryStillValid = true; dirEntryRemain = size; endOfDirectory = false; - + while (entryStillValid && !endOfDirectory && (dirEntryRemain > 0)) { - _FAT_cache_readPartialSector (partition->cache, entryData, - _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + _FAT_cache_readPartialSector (partition->cache, entryData, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); if (entryData[0] == DIR_ENTRY_LAST) { gapStart = gapEnd; @@ -706,7 +706,7 @@ static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, } else { dirEntryRemain = size; } - + if (!endOfDirectory && (dirEntryRemain > 0)) { entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &gapEnd, true); } @@ -731,7 +731,7 @@ static bool _FAT_directory_findEntryGap (PARTITION* partition, DIR_ENTRY* entry, -- dirEntryRemain; // Fill the entry with blanks _FAT_cache_writePartialSector (partition->cache, entryData, - _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, + _FAT_fat_clusterToSector(partition, gapEnd.cluster) + gapEnd.sector, gapEnd.offset * DIR_ENTRY_DATA_SIZE, DIR_ENTRY_DATA_SIZE); } if (!entryStillValid) { @@ -755,7 +755,7 @@ static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, if (dirnameLength >= MAX_FILENAME_LENGTH) { return false; } - + // Make sure the entry doesn't already exist foundFile = _FAT_directory_getFirstEntry (partition, &tempEntry, dirCluster); @@ -776,9 +776,9 @@ static bool _FAT_directory_entryExists (PARTITION* partition, const char* name, return false; } -/* -Creates an alias for a long file name. If the alias is not an exact match for the -filename, it returns the number of characters in the alias. If the two names match, +/* +Creates an alias for a long file name. If the alias is not an exact match for the +filename, it returns the number of characters in the alias. If the two names match, it returns 0. If there was an error, it returns -1. */ static int _FAT_directory_createAlias (char* alias, const char* lfn) { @@ -791,13 +791,13 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) { int bytesUsed = 0; const char* lfnExt; int aliasExtLen; - + // Strip leading periods while (lfn[lfnPos] == '.') { lfnPos ++; lossyConversion = true; } - + // Primary portion of alias while (aliasPos < 8 && lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { bytesUsed = mbrtowc(&lfnChar, lfn + lfnPos, MAX_FILENAME_LENGTH - lfnPos, &ps); @@ -824,17 +824,17 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) { oemChar = '_'; // Replace illegal characters with underscores lossyConversion = true; } - + alias[aliasPos] = (char)oemChar; aliasPos++; lfnPos += bytesUsed; } - + if (lfn[lfnPos] != '.' && lfn[lfnPos] != '\0') { // Name was more than 8 characters long lossyConversion = true; } - + // Alias extension lfnExt = strrchr (lfn, '.'); if (lfnExt != NULL && lfnExt != strchr (lfn, '.')) { @@ -871,7 +871,7 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) { oemChar = '_'; // Replace illegal characters with underscores lossyConversion = true; } - + alias[aliasPos] = (char)oemChar; aliasPos++; lfnExt += bytesUsed; @@ -881,7 +881,7 @@ static int _FAT_directory_createAlias (char* alias, const char* lfn) { lossyConversion = true; } } - + alias[aliasPos] = '\0'; if (lossyConversion) { return aliasPos; @@ -955,14 +955,14 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d } else { // It's a long filename with an alias entrySize = ((lfnLen + LFN_ENTRY_LENGTH - 1) / LFN_ENTRY_LENGTH) + 1; - + // Generate full alias for all cases except when the alias is simply an upper case version of the LFN // and there isn't already a file with that name if (strncasecmp (alias, entry->filename, MAX_ALIAS_LENGTH) != 0 || - _FAT_directory_entryExists (partition, alias, dirCluster)) + _FAT_directory_entryExists (partition, alias, dirCluster)) { // expand primary part to 8 characters long by padding the end with underscores - i = MAX_ALIAS_PRI_LENGTH - 1; + i = MAX_ALIAS_PRI_LENGTH - 1; // Move extension to last 3 characters while (alias[i] != '.' && i > 0) i--; if (i > 0) { @@ -972,7 +972,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d memset (alias + i, '_', j - i); alias[MAX_ALIAS_LENGTH-1]=0; } - + // Generate numeric tail for (i = 1; i <= MAX_NUMERIC_TAIL; i++) { j = i; @@ -1015,7 +1015,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d entry->entryData[j] = ' '; ++ j; } - + // Generate alias checksum for (i=0; i < ALIAS_ENTRY_LENGTH; i++) { // NOTE: The operation is an unsigned char rotate right @@ -1036,7 +1036,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d ucs2_t lfn[MAX_LFN_LENGTH] = {0}; _FAT_directory_mbstoucs2 (lfn, entry->filename, MAX_LFN_LENGTH); - for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; + for (entryStillValid = true, i = entrySize; entryStillValid && i > 0; entryStillValid = _FAT_directory_incrementDirEntryPosition (partition, &curEntryPos, false), -- i ) { if (i > 1) { @@ -1066,7 +1066,7 @@ bool _FAT_directory_addEntry (PARTITION* partition, DIR_ENTRY* entry, uint32_t d } } - return true; + return true; } bool _FAT_directory_chdir (PARTITION* partition, const char* path) { @@ -1097,7 +1097,7 @@ void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct st st->st_uid = 1; // Faked for FAT st->st_gid = 2; // Faked for FAT st->st_rdev = st->st_dev; - st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size + st->st_size = u8array_to_u32 (entry->entryData, DIR_ENTRY_fileSize); // File size st->st_atime = _FAT_filetime_to_time_t ( 0, u8array_to_u16 (entry->entryData, DIR_ENTRY_aDate) @@ -1113,8 +1113,8 @@ void _FAT_directory_entryStat (PARTITION* partition, DIR_ENTRY* entry, struct st u8array_to_u16 (entry->entryData, DIR_ENTRY_cDate) ); st->st_spare3 = 0; - st->st_blksize = BYTES_PER_READ; // Prefered file I/O block size - st->st_blocks = (st->st_size + BYTES_PER_READ - 1) / BYTES_PER_READ; // File size in blocks + st->st_blksize = partition->bytesPerSector; // Prefered file I/O block size + st->st_blocks = (st->st_size + partition->bytesPerSector - 1) / partition->bytesPerSector; // File size in blocks st->st_spare4[0] = 0; st->st_spare4[1] = 0; } diff --git a/libcustomfat/disc.c b/libcustomfat/disc.c index 1fac0ed9..5f626b6b 100644 --- a/libcustomfat/disc.c +++ b/libcustomfat/disc.c @@ -54,6 +54,7 @@ static const DISC_INTERFACE* get_io_usbstorage (void) { static const DISC_INTERFACE* get_io_gcsda (void) { return &__io_gcsda; } + static const DISC_INTERFACE* get_io_gcsdb (void) { return &__io_gcsdb; } @@ -87,7 +88,12 @@ const INTERFACE_ID _FAT_disc_interfaces[] = { #elif defined (NDS) #include +static const DISC_INTERFACE* get_io_dsisd (void) { + return &__io_dsisd; +} + const INTERFACE_ID _FAT_disc_interfaces[] = { + {"sd", get_io_dsisd}, {"fat", dldiGetInternal}, {NULL, NULL} }; diff --git a/libcustomfat/fatfile.c b/libcustomfat/fatfile.c index 293707c1..e2a99f6a 100644 --- a/libcustomfat/fatfile.c +++ b/libcustomfat/fatfile.c @@ -216,8 +216,8 @@ int _FAT_open_r (struct _reent *r, void *fileStruct, const char *path, int flags // Set append pointer to the end of the file file->appendPosition.cluster = _FAT_fat_lastCluster (partition, file->startCluster); - file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ; - file->appendPosition.byte = file->filesize % BYTES_PER_READ; + file->appendPosition.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; + file->appendPosition.byte = file->filesize % partition->bytesPerSector; // Check if the end of the file is on the end of a cluster if ( (file->filesize > 0) && ((file->filesize % partition->bytesPerCluster)==0) ){ @@ -381,12 +381,12 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { cache = file->partition->cache; // Align to sector - tempVar = BYTES_PER_READ - position.byte; + tempVar = partition->bytesPerSector - position.byte; if (tempVar > remain) { tempVar = remain; } - if ((tempVar < BYTES_PER_READ) && flagNoError) + if ((tempVar < partition->bytesPerSector) && flagNoError) { _FAT_cache_readPartialSector ( cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar); @@ -395,7 +395,7 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { ptr += tempVar; position.byte += tempVar; - if (position.byte >= BYTES_PER_READ) { + if (position.byte >= partition->bytesPerSector) { position.byte = 0; position.sector++; } @@ -403,10 +403,10 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { // align to cluster // tempVar is number of sectors to read - if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) { + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { tempVar = partition->sectorsPerCluster - position.sector; } else { - tempVar = remain / BYTES_PER_READ; + tempVar = remain / partition->bytesPerSector; } if ((tempVar > 0) && flagNoError) { @@ -416,8 +416,8 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { flagNoError = false; r->_errno = EIO; } else { - ptr += tempVar * BYTES_PER_READ; - remain -= tempVar * BYTES_PER_READ; + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; position.sector += tempVar; } } @@ -449,12 +449,12 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { chunkSize += partition->bytesPerCluster; } while ((nextChunkStart == chunkEnd + 1) && #ifdef LIMIT_SECTORS - (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) && + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && #endif (chunkSize + partition->bytesPerCluster <= remain)); if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), - chunkSize / BYTES_PER_READ, ptr)) + chunkSize / partition->bytesPerSector, ptr)) { flagNoError = false; r->_errno = EIO; @@ -477,7 +477,7 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { } // Read remaining sectors - tempVar = remain / BYTES_PER_READ; // Number of sectors left + tempVar = remain / partition->bytesPerSector; // Number of sectors left if ((tempVar > 0) && flagNoError) { if (!_FAT_cache_readSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) @@ -485,8 +485,8 @@ ssize_t _FAT_read_r (struct _reent *r, int fd, char *ptr, size_t len) { flagNoError = false; r->_errno = EIO; } else { - ptr += tempVar * BYTES_PER_READ; - remain -= tempVar * BYTES_PER_READ; + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; position.sector += tempVar; } } @@ -555,13 +555,14 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { PARTITION* partition = file->partition; CACHE* cache = file->partition->cache; FILE_POSITION position; - uint8_t zeroBuffer [BYTES_PER_READ] = {0}; + uint8_t zeroBuffer [partition->bytesPerSector]; + memset(zeroBuffer, 0, partition->bytesPerSector); uint32_t remain; uint32_t tempNextCluster; unsigned int sector; - position.byte = file->filesize % BYTES_PER_READ; - position.sector = (file->filesize % partition->bytesPerCluster) / BYTES_PER_READ; + position.byte = file->filesize % partition->bytesPerSector; + position.sector = (file->filesize % partition->bytesPerCluster) / partition->bytesPerSector; // It is assumed that there is always a startCluster // This will be true when _FAT_file_extend_r is called from _FAT_write_r position.cluster = _FAT_fat_lastCluster (partition, file->startCluster); @@ -580,7 +581,7 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { position.sector = 0; } - if (remain + position.byte < BYTES_PER_READ) { + if (remain + position.byte < partition->bytesPerSector) { // Only need to clear to the end of the sector _FAT_cache_writePartialSector (cache, zeroBuffer, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, remain); @@ -589,13 +590,13 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { if (position.byte > 0) { _FAT_cache_writePartialSector (cache, zeroBuffer, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, - BYTES_PER_READ - position.byte); - remain -= (BYTES_PER_READ - position.byte); + partition->bytesPerSector - position.byte); + remain -= (partition->bytesPerSector - position.byte); position.byte = 0; position.sector ++; } - while (remain >= BYTES_PER_READ) { + while (remain >= partition->bytesPerSector) { if (position.sector >= partition->sectorsPerCluster) { position.sector = 0; // Ran out of clusters so get a new one @@ -611,7 +612,7 @@ static bool _FAT_file_extend_r (struct _reent *r, FILE_STRUCT* file) { sector = _FAT_fat_clusterToSector (partition, position.cluster) + position.sector; _FAT_cache_writeSectors (cache, sector, 1, zeroBuffer); - remain -= BYTES_PER_READ; + remain -= partition->bytesPerSector; position.sector ++; } @@ -712,12 +713,12 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); // Align to sector - tempVar = BYTES_PER_READ - position.byte; + tempVar = partition->bytesPerSector - position.byte; if (tempVar > remain) { tempVar = remain; } - if ((tempVar < BYTES_PER_READ) && flagNoError) { + if ((tempVar < partition->bytesPerSector) && flagNoError) { // Write partial sector to disk _FAT_cache_writePartialSector (cache, ptr, _FAT_fat_clusterToSector (partition, position.cluster) + position.sector, position.byte, tempVar); @@ -728,7 +729,7 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { // Move onto next sector - if (position.byte >= BYTES_PER_READ) { + if (position.byte >= partition->bytesPerSector) { position.byte = 0; position.sector ++; } @@ -736,10 +737,10 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { // Align to cluster // tempVar is number of sectors to write - if (remain > (partition->sectorsPerCluster - position.sector) * BYTES_PER_READ) { + if (remain > (partition->sectorsPerCluster - position.sector) * partition->bytesPerSector) { tempVar = partition->sectorsPerCluster - position.sector; } else { - tempVar = remain / BYTES_PER_READ; + tempVar = remain / partition->bytesPerSector; } if ((tempVar > 0 && tempVar < partition->sectorsPerCluster) && flagNoError) { @@ -749,8 +750,8 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { flagNoError = false; r->_errno = EIO; } else { - ptr += tempVar * BYTES_PER_READ; - remain -= tempVar * BYTES_PER_READ; + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; position.sector += tempVar; } } @@ -769,11 +770,11 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { // group consecutive clusters while (flagNoError && #ifdef LIMIT_SECTORS - (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * BYTES_PER_READ) && + (chunkSize + partition->bytesPerCluster <= LIMIT_SECTORS * partition->bytesPerSector) && #endif (chunkSize + partition->bytesPerCluster < remain)) { - // pretend to use up all sectors in next_position + // pretend to use up all sectors in next_position next_position.sector = partition->sectorsPerCluster; // get or allocate next cluster _FAT_check_position_for_next_cluster(r, &next_position, partition, @@ -786,7 +787,7 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { } if ( !_FAT_cache_writeSectors (cache, - _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / BYTES_PER_READ, ptr)) + _FAT_fat_clusterToSector(partition, position.cluster), chunkSize / partition->bytesPerSector, ptr)) { flagNoError = false; r->_errno = EIO; @@ -810,15 +811,15 @@ ssize_t _FAT_write_r (struct _reent *r, int fd, const char *ptr, size_t len) { _FAT_check_position_for_next_cluster(r, &position, partition, remain, &flagNoError); // Write remaining sectors - tempVar = remain / BYTES_PER_READ; // Number of sectors left + tempVar = remain / partition->bytesPerSector; // Number of sectors left if ((tempVar > 0) && flagNoError) { if (!_FAT_cache_writeSectors (cache, _FAT_fat_clusterToSector (partition, position.cluster), tempVar, ptr)) { flagNoError = false; r->_errno = EIO; } else { - ptr += tempVar * BYTES_PER_READ; - remain -= tempVar * BYTES_PER_READ; + ptr += tempVar * partition->bytesPerSector; + remain -= tempVar * partition->bytesPerSector; position.sector += tempVar; } } @@ -927,8 +928,8 @@ off_t _FAT_seek_r (struct _reent *r, int fd, off_t pos, int dir) { } // Calculate the sector and byte of the current position, // and store them - file->rwPosition.sector = (position % partition->bytesPerCluster) / BYTES_PER_READ; - file->rwPosition.byte = position % BYTES_PER_READ; + file->rwPosition.sector = (position % partition->bytesPerCluster) / partition->bytesPerSector; + file->rwPosition.byte = position % partition->bytesPerSector; nextCluster = _FAT_fat_nextCluster (partition, cluster); while ((clusCount > 0) && (nextCluster != CLUSTER_FREE) && (nextCluster != CLUSTER_EOF)) { @@ -1087,13 +1088,13 @@ int _FAT_ftruncate_r (struct _reent *r, int fd, off_t len) { lastCluster = _FAT_fat_trimChain (partition, file->startCluster, chainLength); if (file->append) { - file->appendPosition.byte = newSize % BYTES_PER_READ; + file->appendPosition.byte = newSize % partition->bytesPerSector; // Does the end of the file fall on the edge of a cluster? if (newSize % partition->bytesPerCluster == 0) { // Set a flag to allocate a new cluster file->appendPosition.sector = partition->sectorsPerCluster; } else { - file->appendPosition.sector = (newSize % partition->bytesPerCluster) / BYTES_PER_READ; + file->appendPosition.sector = (newSize % partition->bytesPerCluster) / partition->bytesPerSector; } file->appendPosition.cluster = lastCluster; } diff --git a/libcustomfat/fatfile_frag.c b/libcustomfat/fatfile_frag.c index 3ec65b21..340b7155 100644 --- a/libcustomfat/fatfile_frag.c +++ b/libcustomfat/fatfile_frag.c @@ -32,7 +32,7 @@ int _FAT_get_fragments (const char *path, _fat_frag_append_t append_fragment, vo partition = file.partition; _FAT_lock(&partition->lock); - size = file.filesize / BYTES_PER_READ; + size = file.filesize / partition->bytesPerSector; cluster = file.startCluster; offset = 0; diff --git a/libcustomfat/file_allocation_table.c b/libcustomfat/file_allocation_table.c index 76902099..9a3cc884 100644 --- a/libcustomfat/file_allocation_table.c +++ b/libcustomfat/file_allocation_table.c @@ -30,6 +30,7 @@ #include "file_allocation_table.h" #include "partition.h" +#include "mem_allocate.h" #include /* @@ -54,15 +55,15 @@ uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) case FS_FAT12: { u32 nextCluster_h; - sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ); - offset = ((cluster * 3) / 2) % BYTES_PER_READ; + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u8)); offset++; - if (offset >= BYTES_PER_READ) { + if (offset >= partition->bytesPerSector) { offset = 0; sector++; } @@ -85,8 +86,8 @@ uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) break; } case FS_FAT16: - sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ); - offset = (cluster % (BYTES_PER_READ >> 1)) << 1; + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u16)); @@ -96,8 +97,8 @@ uint32_t _FAT_fat_nextCluster(PARTITION* partition, uint32_t cluster) break; case FS_FAT32: - sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ); - offset = (cluster % (BYTES_PER_READ >> 2)) << 2; + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; _FAT_cache_readLittleEndianValue (partition->cache, &nextCluster, sector, offset, sizeof(u32)); @@ -135,8 +136,8 @@ static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint break; case FS_FAT12: - sector = partition->fat.fatStart + (((cluster * 3) / 2) / BYTES_PER_READ); - offset = ((cluster * 3) / 2) % BYTES_PER_READ; + sector = partition->fat.fatStart + (((cluster * 3) / 2) / partition->bytesPerSector); + offset = ((cluster * 3) / 2) % partition->bytesPerSector; if (cluster & 0x01) { @@ -147,7 +148,7 @@ static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint _FAT_cache_writeLittleEndianValue (partition->cache, value & 0xFF, sector, offset, sizeof(u8)); offset++; - if (offset >= BYTES_PER_READ) { + if (offset >= partition->bytesPerSector) { offset = 0; sector++; } @@ -159,7 +160,7 @@ static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u8)); offset++; - if (offset >= BYTES_PER_READ) { + if (offset >= partition->bytesPerSector) { offset = 0; sector++; } @@ -174,16 +175,16 @@ static bool _FAT_fat_writeFatEntry (PARTITION* partition, uint32_t cluster, uint break; case FS_FAT16: - sector = partition->fat.fatStart + ((cluster << 1) / BYTES_PER_READ); - offset = (cluster % (BYTES_PER_READ >> 1)) << 1; + sector = partition->fat.fatStart + ((cluster << 1) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 1)) << 1; _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u16)); break; case FS_FAT32: - sector = partition->fat.fatStart + ((cluster << 2) / BYTES_PER_READ); - offset = (cluster % (BYTES_PER_READ >> 2)) << 2; + sector = partition->fat.fatStart + ((cluster << 2) / partition->bytesPerSector); + offset = (cluster % (partition->bytesPerSector >> 2)) << 2; _FAT_cache_writeLittleEndianValue (partition->cache, value, sector, offset, sizeof(u32)); @@ -269,7 +270,7 @@ If an error occurs, return CLUSTER_ERROR uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster) { uint32_t newCluster; uint32_t i; - uint8_t emptySector[BYTES_PER_READ]; + uint8_t *emptySector; // Link the cluster newCluster = _FAT_fat_linkFreeCluster(partition, cluster); @@ -278,14 +279,18 @@ uint32_t _FAT_fat_linkFreeClusterCleared (PARTITION* partition, uint32_t cluster return CLUSTER_ERROR; } + emptySector = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + // Clear all the sectors within the cluster - memset (emptySector, 0, BYTES_PER_READ); + memset (emptySector, 0, partition->bytesPerSector); for (i = 0; i < partition->sectorsPerCluster; i++) { _FAT_cache_writeSectors (partition->cache, _FAT_fat_clusterToSector (partition, newCluster) + i, 1, emptySector); } + _FAT_mem_free(emptySector); + return newCluster; } diff --git a/libcustomfat/partition.c b/libcustomfat/partition.c index 40ce8983..41b9d761 100644 --- a/libcustomfat/partition.c +++ b/libcustomfat/partition.c @@ -40,11 +40,6 @@ sec_t _FAT_startSector; -/* -This device name, as known by devkitPro toolchains -*/ -const char* DEVICE_NAME = "fat"; - /* Data offsets */ @@ -110,17 +105,20 @@ static const char FAT_SIG[3] = {'F', 'A', 'T'}; static const char FS_INFO_SIG1[4] = {'R', 'R', 'a', 'A'}; static const char FS_INFO_SIG2[4] = {'r', 'r', 'A', 'a'}; - sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) { uint8_t part_table[16*4]; uint8_t *ptr; int i; - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_allocate(MAX_SECTOR_SIZE); + if(!sectorBuffer) { + return 0; + } // Read first sector of disc if (!_FAT_disc_readSectors (disc, 0, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); return 0; } @@ -132,6 +130,7 @@ sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + _FAT_mem_free(sectorBuffer); return part_lba; } @@ -144,43 +143,61 @@ sec_t FindFirstValidPartition(const DISC_INTERFACE* disc) for(n=0;n<8;n++) // max 8 logic partitions { - if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) return 0; + if(!_FAT_disc_readSectors (disc, part_lba+next_lba2, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return 0; + } part_lba2 = part_lba + next_lba2 + u8array_to_u32(sectorBuffer, 0x1C6) ; next_lba2 = u8array_to_u32(sectorBuffer, 0x1D6); - if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) return 0; + if(!_FAT_disc_readSectors (disc, part_lba2, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return 0; + } if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || - !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) - { + !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + _FAT_mem_free(sectorBuffer); return part_lba2; } if(next_lba2==0) break; } } else { - if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) return 0; + if(!_FAT_disc_readSectors (disc, part_lba, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); + return 0; + } if (!memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) || !memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + _FAT_mem_free(sectorBuffer); return part_lba; } } } + _FAT_mem_free(sectorBuffer); return 0; } + PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cacheSize, uint32_t sectorsPerPage, sec_t startSector) { PARTITION* partition; - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; + + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_allocate(MAX_SECTOR_SIZE); + if(!sectorBuffer) { + return NULL; + } // Read first sector of disc if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); return NULL; } // Make sure it is a valid MBR or boot sector if ( (sectorBuffer[BPB_bootSig_55] != 0x55) || (sectorBuffer[BPB_bootSig_AA] != 0xAA)) { + _FAT_mem_free(sectorBuffer); return NULL; } @@ -195,27 +212,29 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach } else { startSector = FindFirstValidPartition(disc); if (!_FAT_disc_readSectors (disc, startSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); return NULL; } } + _FAT_startSector = startSector; + // Now verify that this is indeed a FAT partition if (memcmp(sectorBuffer + BPB_FAT16_fileSysType, FAT_SIG, sizeof(FAT_SIG)) && - memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) - { + memcmp(sectorBuffer + BPB_FAT32_fileSysType, FAT_SIG, sizeof(FAT_SIG))) { + _FAT_mem_free(sectorBuffer); return NULL; } partition = (PARTITION*) _FAT_mem_allocate (sizeof(PARTITION)); if (partition == NULL) { + _FAT_mem_free(sectorBuffer); return NULL; } // 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 @@ -236,8 +255,19 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach partition->numberOfSectors = u8array_to_u32( sectorBuffer, BPB_numSectors); } - partition->bytesPerSector = BYTES_PER_READ; // Sector size is redefined to be 512 bytes - partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / BYTES_PER_READ; + if(disc->features & FEATURE_WII_USB) + partition->bytesPerSector = u8array_to_u16(sectorBuffer, BPB_bytesPerSector); + else + partition->bytesPerSector = MIN_SECTOR_SIZE; + + if(partition->bytesPerSector < MIN_SECTOR_SIZE || partition->bytesPerSector > MAX_SECTOR_SIZE) { + // Unsupported sector size + _FAT_mem_free(sectorBuffer); + _FAT_mem_free(partition); + return NULL; + } + + partition->sectorsPerCluster = sectorBuffer[BPB_sectorsPerCluster] * u8array_to_u16(sectorBuffer, BPB_bytesPerSector) / partition->bytesPerSector; partition->bytesPerCluster = partition->bytesPerSector * partition->sectorsPerCluster; partition->fat.fatStart = startSector + u8array_to_u16(sectorBuffer, BPB_reservedSectors); @@ -278,7 +308,7 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach } // Create a cache to use - partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors); + partition->cache = _FAT_cache_constructor (cacheSize, sectorsPerPage, partition->disc, startSector+partition->numberOfSectors, partition->bytesPerSector); // Set current directory to the root partition->cwdCluster = partition->rootDirCluster; @@ -292,6 +322,8 @@ PARTITION* _FAT_partition_constructor (const DISC_INTERFACE* disc, uint32_t cach _FAT_partition_readFSinfo(partition); + _FAT_mem_free(sectorBuffer); + return partition; } @@ -338,8 +370,11 @@ void _FAT_partition_createFSinfo(PARTITION * partition) if(partition->readOnly || partition->filesysType != FS_FAT32) return; - uint8_t sectorBuffer[BYTES_PER_READ]; - memset(sectorBuffer, 0, sizeof(sectorBuffer)); + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + if(!sectorBuffer) { + return; + } + memset(sectorBuffer, 0, partition->bytesPerSector); int i; for(i = 0; i < 4; ++i) @@ -356,6 +391,8 @@ void _FAT_partition_createFSinfo(PARTITION * partition) sectorBuffer[FSIB_bootSig_AA] = 0xAA; _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + + _FAT_mem_free(sectorBuffer); } void _FAT_partition_readFSinfo(PARTITION * partition) @@ -363,24 +400,29 @@ void _FAT_partition_readFSinfo(PARTITION * partition) if(partition->filesysType != FS_FAT32) return; - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; - + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + if(!sectorBuffer) { + return; + } + memset(sectorBuffer, 0, partition->bytesPerSector); // Read first sector of disc if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); return; } if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) != 0 || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4) != 0 || - u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) - { + u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster) == 0) { //sector does not yet exist, create one! _FAT_partition_createFSinfo(partition); + _FAT_mem_free(sectorBuffer); return; } partition->fat.numberFreeCluster = u8array_to_u32(sectorBuffer, FSIB_numberOfFreeCluster); partition->fat.numberLastAllocCluster = u8array_to_u32(sectorBuffer, FSIB_numberLastAllocCluster); + _FAT_mem_free(sectorBuffer); } void _FAT_partition_writeFSinfo(PARTITION * partition) @@ -388,21 +430,26 @@ void _FAT_partition_writeFSinfo(PARTITION * partition) if(partition->filesysType != FS_FAT32) return; - uint8_t sectorBuffer[BYTES_PER_READ] = {0}; - + uint8_t *sectorBuffer = (uint8_t*) _FAT_mem_allocate(partition->bytesPerSector); + if(!sectorBuffer) { + return; + } + memset(sectorBuffer, 0, partition->bytesPerSector); // Read first sector of disc if (!_FAT_disc_readSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { + _FAT_mem_free(sectorBuffer); return; } - if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) + if(memcmp(sectorBuffer+FSIB_SIG1, FS_INFO_SIG1, 4) || memcmp(sectorBuffer+FSIB_SIG2, FS_INFO_SIG2, 4)) { + _FAT_mem_free(sectorBuffer); return; + } u32_to_u8array(sectorBuffer, FSIB_numberOfFreeCluster, partition->fat.numberFreeCluster); u32_to_u8array(sectorBuffer, FSIB_numberLastAllocCluster, partition->fat.numberLastAllocCluster); // Read first sector of disc - if (!_FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer)) { - return; - } + _FAT_disc_writeSectors (partition->disc, partition->fsInfoSector, 1, sectorBuffer); + _FAT_mem_free(sectorBuffer); } diff --git a/libcustomfat/partition.h b/libcustomfat/partition.h index b8ad0b17..ec27a0eb 100644 --- a/libcustomfat/partition.h +++ b/libcustomfat/partition.h @@ -34,8 +34,8 @@ #include "cache.h" #include "lock.h" -// Device name -extern const char* DEVICE_NAME; +#define MIN_SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 // Filesystem type typedef enum {FS_UNKNOWN, FS_FAT12, FS_FAT16, FS_FAT32} FS_TYPE; diff --git a/libcustomntfs/attrib_frag.c b/libcustomntfs/attrib_frag.c index ebde221b..491e8fe5 100644 --- a/libcustomntfs/attrib_frag.c +++ b/libcustomntfs/attrib_frag.c @@ -62,756 +62,20 @@ #include "ntfs.h" #include "ntfsfile_frag.h" -#if 0 - -#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'), - const_cpu_to_le16('D'), - const_cpu_to_le16('S'), - const_cpu_to_le16('\0') }; - -static int NAttrFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) +static u8 size_to_shift(u32 size) { - if (na->type == AT_DATA && na->name == AT_UNNAMED) - return (na->ni->flags & flag); - return 0; + u8 ret = 0; + while (size) + { + ret++; + size >>= 1; + } + return ret - 1; } -static void NAttrSetFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) -{ - if (na->type == AT_DATA && na->name == AT_UNNAMED) - na->ni->flags |= flag; - else - ntfs_log_trace("Denied setting flag %d for not unnamed data " - "attribute\n", flag); -} - -static void NAttrClearFlag(ntfs_attr *na, FILE_ATTR_FLAGS flag) -{ - if (na->type == AT_DATA && na->name == AT_UNNAMED) - na->ni->flags &= ~flag; -} - -#define GenNAttrIno(func_name, flag) \ -int NAttr##func_name(ntfs_attr *na) { return NAttrFlag (na, flag); } \ -void NAttrSet##func_name(ntfs_attr *na) { NAttrSetFlag (na, flag); } \ -void NAttrClear##func_name(ntfs_attr *na){ NAttrClearFlag(na, flag); } - -GenNAttrIno(Compressed, FILE_ATTR_COMPRESSED) -GenNAttrIno(Encrypted, FILE_ATTR_ENCRYPTED) -GenNAttrIno(Sparse, FILE_ATTR_SPARSE_FILE) - -/** - * ntfs_get_attribute_value_length - Find the length of an attribute - * @a: - * - * Description... - * - * Returns: - */ -s64 ntfs_get_attribute_value_length(const ATTR_RECORD *a) -{ - if (!a) { - errno = EINVAL; - return 0; - } - errno = 0; - if (a->non_resident) - return sle64_to_cpu(a->data_size); - - return (s64)le32_to_cpu(a->value_length); -} - -/** - * ntfs_get_attribute_value - Get a copy of an attribute - * @vol: - * @a: - * @b: - * - * Description... - * - * Returns: - */ -s64 ntfs_get_attribute_value(const ntfs_volume *vol, - const ATTR_RECORD *a, u8 *b) -{ - runlist *rl; - s64 total, r; - int i; - - /* Sanity checks. */ - if (!vol || !a || !b) { - errno = EINVAL; - return 0; - } - /* Complex attribute? */ - /* - * Ignore the flags in case they are not zero for an attribute list - * attribute. Windows does not complain about invalid flags and chkdsk - * does not detect or fix them so we need to cope with it, too. - */ - if (a->type != AT_ATTRIBUTE_LIST && a->flags) { - ntfs_log_error("Non-zero (%04x) attribute flags. Cannot handle " - "this yet.\n", le16_to_cpu(a->flags)); - errno = EOPNOTSUPP; - return 0; - } - if (!a->non_resident) { - /* Attribute is resident. */ - - /* Sanity check. */ - if (le32_to_cpu(a->value_length) + le16_to_cpu(a->value_offset) - > le32_to_cpu(a->length)) { - return 0; - } - - memcpy(b, (const char*)a + le16_to_cpu(a->value_offset), - le32_to_cpu(a->value_length)); - errno = 0; - return (s64)le32_to_cpu(a->value_length); - } - - /* Attribute is not resident. */ - - /* If no data, return 0. */ - if (!(a->data_size)) { - errno = 0; - return 0; - } - /* - * FIXME: What about attribute lists?!? (AIA) - */ - /* Decompress the mapping pairs array into a runlist. */ - rl = ntfs_mapping_pairs_decompress(vol, a, NULL); - if (!rl) { - errno = EINVAL; - return 0; - } - /* - * FIXED: We were overflowing here in a nasty fashion when we - * reach the last cluster in the runlist as the buffer will - * only be big enough to hold data_size bytes while we are - * reading in allocated_size bytes which is usually larger - * than data_size, since the actual data is unlikely to have a - * size equal to a multiple of the cluster size! - * FIXED2: We were also overflowing here in the same fashion - * when the data_size was more than one run smaller than the - * allocated size which happens with Windows XP sometimes. - */ - /* Now load all clusters in the runlist into b. */ - for (i = 0, total = 0; rl[i].length; i++) { - if (total + (rl[i].length << vol->cluster_size_bits) >= - sle64_to_cpu(a->data_size)) { - unsigned char *intbuf = NULL; - /* - * We have reached the last run so we were going to - * overflow when executing the ntfs_pread() which is - * BAAAAAAAD! - * Temporary fix: - * Allocate a new buffer with size: - * rl[i].length << vol->cluster_size_bits, do the - * read into our buffer, then memcpy the correct - * amount of data into the caller supplied buffer, - * free our buffer, and continue. - * We have reached the end of data size so we were - * going to overflow in the same fashion. - * Temporary fix: same as above. - */ - intbuf = ntfs_malloc(rl[i].length << vol->cluster_size_bits); - if (!intbuf) { - free(rl); - return 0; - } - /* - * FIXME: If compressed file: Only read if lcn != -1. - * Otherwise, we are dealing with a sparse run and we - * just memset the user buffer to 0 for the length of - * the run, which should be 16 (= compression unit - * size). - * FIXME: Really only when file is compressed, or can - * we have sparse runs in uncompressed files as well? - * - Yes we can, in sparse files! But not necessarily - * size of 16, just run length. - */ - r = ntfs_pread(vol->dev, rl[i].lcn << - vol->cluster_size_bits, rl[i].length << - vol->cluster_size_bits, intbuf); - if (r != rl[i].length << vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) - ntfs_log_perror(ESTR); - else if (r < rl[i].length << - vol->cluster_size_bits) { - ntfs_log_debug(ESTR ": Ran out of input data.\n"); - errno = EIO; - } else { - ntfs_log_debug(ESTR ": unknown error\n"); - errno = EIO; - } -#undef ESTR - free(rl); - free(intbuf); - return 0; - } - memcpy(b + total, intbuf, sle64_to_cpu(a->data_size) - - total); - free(intbuf); - total = sle64_to_cpu(a->data_size); - break; - } - /* - * FIXME: If compressed file: Only read if lcn != -1. - * Otherwise, we are dealing with a sparse run and we just - * memset the user buffer to 0 for the length of the run, which - * should be 16 (= compression unit size). - * FIXME: Really only when file is compressed, or can - * we have sparse runs in uncompressed files as well? - * - Yes we can, in sparse files! But not necessarily size of - * 16, just run length. - */ - r = ntfs_pread(vol->dev, rl[i].lcn << vol->cluster_size_bits, - rl[i].length << vol->cluster_size_bits, - b + total); - if (r != rl[i].length << vol->cluster_size_bits) { -#define ESTR "Error reading attribute value" - if (r == -1) - ntfs_log_perror(ESTR); - else if (r < rl[i].length << vol->cluster_size_bits) { - ntfs_log_debug(ESTR ": Ran out of input data.\n"); - errno = EIO; - } else { - ntfs_log_debug(ESTR ": unknown error\n"); - errno = EIO; - } -#undef ESTR - free(rl); - return 0; - } - total += r; - } - free(rl); - return total; -} - -/* Already cleaned up code below, but still look for FIXME:... */ - -/** - * __ntfs_attr_init - primary initialization of an ntfs attribute structure - * @na: ntfs attribute to initialize - * @ni: ntfs inode with which to initialize the ntfs attribute - * @type: attribute type - * @name: attribute name in little endian Unicode or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * - * Initialize the ntfs attribute @na with @ni, @type, @name, and @name_len. - */ -static void __ntfs_attr_init(ntfs_attr *na, ntfs_inode *ni, - const ATTR_TYPES type, ntfschar *name, const u32 name_len) -{ - na->rl = NULL; - na->ni = ni; - na->type = type; - na->name = name; - if (name) - na->name_len = name_len; - else - na->name_len = 0; -} - -/** - * ntfs_attr_init - initialize an ntfs_attr with data sizes and status - * @na: - * @non_resident: - * @compressed: - * @encrypted: - * @sparse: - * @allocated_size: - * @data_size: - * @initialized_size: - * @compressed_size: - * @compression_unit: - * - * Final initialization for an ntfs attribute. - */ -void ntfs_attr_init(ntfs_attr *na, const BOOL non_resident, - const ATTR_FLAGS data_flags, - const BOOL encrypted, const BOOL sparse, - const s64 allocated_size, const s64 data_size, - const s64 initialized_size, const s64 compressed_size, - const u8 compression_unit) -{ - if (!NAttrInitialized(na)) { - na->data_flags = data_flags; - if (non_resident) - NAttrSetNonResident(na); - if (data_flags & ATTR_COMPRESSION_MASK) - NAttrSetCompressed(na); - if (encrypted) - NAttrSetEncrypted(na); - if (sparse) - NAttrSetSparse(na); - na->allocated_size = allocated_size; - na->data_size = data_size; - na->initialized_size = initialized_size; - if ((data_flags & ATTR_COMPRESSION_MASK) || sparse) { - ntfs_volume *vol = na->ni->vol; - - na->compressed_size = compressed_size; - na->compression_block_clusters = 1 << compression_unit; - na->compression_block_size = 1 << (compression_unit + - vol->cluster_size_bits); - na->compression_block_size_bits = ffs( - na->compression_block_size) - 1; - } - NAttrSetInitialized(na); - } -} - -/** - * ntfs_attr_open - open an ntfs attribute for access - * @ni: open ntfs inode in which the ntfs attribute resides - * @type: attribute type - * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * - * Allocate a new ntfs attribute structure, initialize it with @ni, @type, - * @name, and @name_len, then return it. Return NULL on error with - * errno set to the error code. - * - * If @name is AT_UNNAMED look specifically for an unnamed attribute. If you - * do not care whether the attribute is named or not set @name to NULL. In - * both those cases @name_len is not used at all. - */ -ntfs_attr *ntfs_attr_open(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len) -{ - ntfs_attr_search_ctx *ctx; - ntfs_attr *na = NULL; - ntfschar *newname = NULL; - ATTR_RECORD *a; - BOOL cs; - - ntfs_log_enter("Entering for inode %lld, attr 0x%x.\n", - (unsigned long long)ni->mft_no, type); - - if (!ni || !ni->vol || !ni->mrec) { - errno = EINVAL; - goto out; - } - na = ntfs_calloc(sizeof(ntfs_attr)); - if (!na) - goto out; - if (name && name != AT_UNNAMED && name != NTFS_INDEX_I30) { - name = ntfs_ucsndup(name, name_len); - if (!name) - goto err_out; - newname = name; - } - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - goto err_out; - - if (ntfs_attr_lookup(type, name, name_len, 0, 0, NULL, 0, ctx)) - goto put_err_out; - - a = ctx->attr; - - if (!name) { - if (a->name_length) { - name = ntfs_ucsndup((ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length); - if (!name) - goto put_err_out; - newname = name; - name_len = a->name_length; - } else { - name = AT_UNNAMED; - name_len = 0; - } - } - - __ntfs_attr_init(na, ni, type, name, name_len); - - /* - * Wipe the flags in case they are not zero for an attribute list - * attribute. Windows does not complain about invalid flags and chkdsk - * does not detect or fix them so we need to cope with it, too. - */ - if (type == AT_ATTRIBUTE_LIST) - a->flags = 0; - - if ((type == AT_DATA) && !a->initialized_size) { - /* - * Define/redefine the compression state if stream is - * empty, based on the compression mark on parent - * directory (for unnamed data streams) or on current - * inode (for named data streams). The compression mark - * may change any time, the compression state can only - * change when stream is wiped out. - */ - a->flags &= ~ATTR_COMPRESSION_MASK; - if (na->ni->flags & FILE_ATTR_COMPRESSED) - a->flags |= ATTR_IS_COMPRESSED; - } - - cs = a->flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE); - - if (na->type == AT_DATA && na->name == AT_UNNAMED && - ((!(a->flags & ATTR_IS_SPARSE) != !NAttrSparse(na)) || - (!(a->flags & ATTR_IS_ENCRYPTED) != !NAttrEncrypted(na)))) { - errno = EIO; - ntfs_log_perror("Inode %lld has corrupt attribute flags " - "(0x%x <> 0x%x)",(unsigned long long)ni->mft_no, - a->flags, na->ni->flags); - goto put_err_out; - } - - if (a->non_resident) { - if ((a->flags & ATTR_COMPRESSION_MASK) - && !a->compression_unit) { - errno = EIO; - ntfs_log_perror("Compressed inode %lld attr 0x%x has " - "no compression unit", - (unsigned long long)ni->mft_no, type); - goto put_err_out; - } - ntfs_attr_init(na, TRUE, a->flags, - a->flags & ATTR_IS_ENCRYPTED, - a->flags & ATTR_IS_SPARSE, - sle64_to_cpu(a->allocated_size), - sle64_to_cpu(a->data_size), - sle64_to_cpu(a->initialized_size), - cs ? sle64_to_cpu(a->compressed_size) : 0, - cs ? a->compression_unit : 0); - } else { - s64 l = le32_to_cpu(a->value_length); - ntfs_attr_init(na, FALSE, a->flags, - a->flags & ATTR_IS_ENCRYPTED, - a->flags & ATTR_IS_SPARSE, (l + 7) & ~7, l, l, - cs ? (l + 7) & ~7 : 0, 0); - } - ntfs_attr_put_search_ctx(ctx); -out: - ntfs_log_leave("\n"); - return na; - -put_err_out: - ntfs_attr_put_search_ctx(ctx); -err_out: - free(newname); - free(na); - na = NULL; - goto out; -} - -/** - * ntfs_attr_close - free an ntfs attribute structure - * @na: ntfs attribute structure to free - * - * Release all memory associated with the ntfs attribute @na and then release - * @na itself. - */ -void ntfs_attr_close(ntfs_attr *na) -{ - if (!na) - return; - if (NAttrNonResident(na) && na->rl) - free(na->rl); - /* Don't release if using an internal constant. */ - if (na->name != AT_UNNAMED && na->name != NTFS_INDEX_I30 - && na->name != STREAM_SDS) - free(na->name); - free(na); -} - -/** - * ntfs_attr_map_runlist - map (a part of) a runlist of an ntfs attribute - * @na: ntfs attribute for which to map (part of) a runlist - * @vcn: map runlist part containing this vcn - * - * Map the part of a runlist containing the @vcn of the ntfs attribute @na. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_map_runlist(ntfs_attr *na, VCN vcn) -{ - LCN lcn; - ntfs_attr_search_ctx *ctx; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, (long long)vcn); - - lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); - if (lcn >= 0 || lcn == LCN_HOLE || lcn == LCN_ENOENT) - return 0; - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - - /* Find the attribute in the mft record. */ - if (!ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - vcn, NULL, 0, ctx)) { - runlist_element *rl; - - /* Decode the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, ctx->attr, - na->rl); - if (rl) { - na->rl = rl; - ntfs_attr_put_search_ctx(ctx); - return 0; - } - } - - ntfs_attr_put_search_ctx(ctx); - return -1; -} - -/** - * ntfs_attr_map_whole_runlist - map the whole runlist of an ntfs attribute - * @na: ntfs attribute for which to map the runlist - * - * Map the whole runlist of the ntfs attribute @na. For an attribute made up - * of only one attribute extent this is the same as calling - * ntfs_attr_map_runlist(na, 0) but for an attribute with multiple extents this - * will map the runlist fragments from each of the extents thus giving access - * to the entirety of the disk allocation of an attribute. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_map_whole_runlist(ntfs_attr *na) -{ - VCN next_vcn, last_vcn, highest_vcn; - ntfs_attr_search_ctx *ctx; - ntfs_volume *vol = na->ni->vol; - ATTR_RECORD *a; - int ret = -1; - - ntfs_log_enter("Entering for inode %llu, attr 0x%x.\n", - (unsigned long long)na->ni->mft_no, na->type); - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto out; - - /* Map all attribute extents one by one. */ - next_vcn = last_vcn = highest_vcn = 0; - a = NULL; - while (1) { - runlist_element *rl; - - int not_mapped = 0; - if (ntfs_rl_vcn_to_lcn(na->rl, next_vcn) == LCN_RL_NOT_MAPPED) - not_mapped = 1; - - if (ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, next_vcn, NULL, 0, ctx)) - break; - - a = ctx->attr; - - if (not_mapped) { - /* Decode the runlist. */ - rl = ntfs_mapping_pairs_decompress(na->ni->vol, - a, na->rl); - if (!rl) - goto err_out; - na->rl = rl; - } - - /* Are we in the first extent? */ - if (!next_vcn) { - if (a->lowest_vcn) { - errno = EIO; - ntfs_log_perror("First extent of inode %llu " - "attribute has non-zero lowest_vcn", - (unsigned long long)na->ni->mft_no); - goto err_out; - } - /* Get the last vcn in the attribute. */ - last_vcn = sle64_to_cpu(a->allocated_size) >> - vol->cluster_size_bits; - } - - /* Get the lowest vcn for the next extent. */ - highest_vcn = sle64_to_cpu(a->highest_vcn); - next_vcn = highest_vcn + 1; - - /* Only one extent or error, which we catch below. */ - if (next_vcn <= 0) { - errno = ENOENT; - break; - } - - /* Avoid endless loops due to corruption. */ - if (next_vcn < sle64_to_cpu(a->lowest_vcn)) { - errno = EIO; - ntfs_log_perror("Inode %llu has corrupt attribute list", - (unsigned long long)na->ni->mft_no); - goto err_out; - } - } - if (!a) { - ntfs_log_perror("Couldn't find attribute for runlist mapping"); - goto err_out; - } - if (highest_vcn && highest_vcn != last_vcn - 1) { - errno = EIO; - ntfs_log_perror("Failed to load full runlist: inode: %llu " - "highest_vcn: 0x%llx last_vcn: 0x%llx", - (unsigned long long)na->ni->mft_no, - (long long)highest_vcn, (long long)last_vcn); - goto err_out; - } - if (errno == ENOENT) - ret = 0; -err_out: - ntfs_attr_put_search_ctx(ctx); -out: - ntfs_log_leave("\n"); - return ret; -} - -/** - * ntfs_attr_vcn_to_lcn - convert a vcn into a lcn given an ntfs attribute - * @na: ntfs attribute whose runlist to use for conversion - * @vcn: vcn to convert - * - * Convert the virtual cluster number @vcn of an attribute into a logical - * cluster number (lcn) of a device using the runlist @na->rl to map vcns to - * their corresponding lcns. - * - * If the @vcn is not mapped yet, attempt to map the attribute extent - * containing the @vcn and retry the vcn to lcn conversion. - * - * Since lcns must be >= 0, we use negative return values with special meaning: - * - * Return value Meaning / Description - * ========================================== - * -1 = LCN_HOLE Hole / not allocated on disk. - * -3 = LCN_ENOENT There is no such vcn in the attribute. - * -4 = LCN_EINVAL Input parameter error. - * -5 = LCN_EIO Corrupt fs, disk i/o error, or not enough memory. - */ -LCN ntfs_attr_vcn_to_lcn(ntfs_attr *na, const VCN vcn) -{ - LCN lcn; - BOOL is_retry = FALSE; - - if (!na || !NAttrNonResident(na) || vcn < 0) - return (LCN)LCN_EINVAL; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); -retry: - /* Convert vcn to lcn. If that fails map the runlist and retry once. */ - lcn = ntfs_rl_vcn_to_lcn(na->rl, vcn); - if (lcn >= 0) - return lcn; - if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { - is_retry = TRUE; - goto retry; - } - /* - * If the attempt to map the runlist failed, or we are getting - * LCN_RL_NOT_MAPPED despite having mapped the attribute extent - * successfully, something is really badly wrong... - */ - if (!is_retry || lcn == (LCN)LCN_RL_NOT_MAPPED) - return (LCN)LCN_EIO; - /* lcn contains the appropriate error code. */ - return lcn; -} - -/** - * ntfs_attr_find_vcn - find a vcn in the runlist of an ntfs attribute - * @na: ntfs attribute whose runlist to search - * @vcn: vcn to find - * - * Find the virtual cluster number @vcn in the runlist of the ntfs attribute - * @na and return the the address of the runlist element containing the @vcn. - * - * Note you need to distinguish between the lcn of the returned runlist - * element being >= 0 and LCN_HOLE. In the later case you have to return zeroes - * on read and allocate clusters on write. You need to update the runlist, the - * attribute itself as well as write the modified mft record to disk. - * - * If there is an error return NULL with errno set to the error code. The - * following error codes are defined: - * EINVAL Input parameter error. - * ENOENT There is no such vcn in the runlist. - * ENOMEM Not enough memory. - * EIO I/O error or corrupt metadata. - */ -runlist_element *ntfs_attr_find_vcn(ntfs_attr *na, const VCN vcn) -{ - runlist_element *rl; - BOOL is_retry = FALSE; - - if (!na || !NAttrNonResident(na) || vcn < 0) { - errno = EINVAL; - return NULL; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, vcn %llx\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)vcn); -retry: - rl = na->rl; - if (!rl) - goto map_rl; - if (vcn < rl[0].vcn) - goto map_rl; - while (rl->length) { - if (vcn < rl[1].vcn) { - if (rl->lcn >= (LCN)LCN_HOLE) - return rl; - break; - } - rl++; - } - switch (rl->lcn) { - case (LCN)LCN_RL_NOT_MAPPED: - goto map_rl; - case (LCN)LCN_ENOENT: - errno = ENOENT; - break; - case (LCN)LCN_EINVAL: - errno = EINVAL; - break; - default: - errno = EIO; - break; - } - return NULL; -map_rl: - /* The @vcn is in an unmapped region, map the runlist and retry. */ - if (!is_retry && !ntfs_attr_map_runlist(na, vcn)) { - is_retry = TRUE; - goto retry; - } - /* - * If we already retried or the mapping attempt failed something has - * gone badly wrong. EINVAL and ENOENT coming from a failed mapping - * attempt are equivalent to errors for us as they should not happen - * in our code paths. - */ - if (is_retry || errno == EINVAL || errno == ENOENT) - errno = EIO; - return NULL; -} - - -#endif - /** * ntfs_attr_pread_i - see description at ntfs_attr_pread() - */ + */ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 offset, _ntfs_frag_append_t append_fragment, void *callback_data) { @@ -822,8 +86,8 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 //u16 efs_padding_length; /* Sanity checking arguments is done in ntfs_attr_pread(). */ - - if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) + + if ((na->data_flags & ATTR_COMPRESSION_MASK) && NAttrNonResident(na)) { //return -1; // no compressed files return -31; @@ -832,7 +96,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 == ATTR_IS_COMPRESSED) return ntfs_compressed_attr_pread(na, pos, count, b); else { - // compression mode not supported + // compression mode not supported errno = EOPNOTSUPP; return -1; } @@ -849,7 +113,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 //return -1; return -32; } - + if (!count) return 0; /* @@ -867,7 +131,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 errno = EINVAL; //return -1; return -33; - } + } max_init = max_read = ((na->data_size + 511) & ~511) + 2; } if (pos + count > max_read) { @@ -876,7 +140,7 @@ static s64 ntfs_attr_getfragments_i(ntfs_attr *na, const s64 pos, s64 count, u64 count = max_read - pos; } /* If it is a resident attribute, get the value from the mft record. */ - if (!NAttrNonResident(na)) + if (!NAttrNonResident(na)) { return -34; // No resident files /* @@ -917,14 +181,14 @@ res_err_out: //memset((u8*)b + count, 0, total2); } /* - * for encrypted non-resident attributes with efs_raw set + * for encrypted non-resident attributes with efs_raw set * the last two bytes aren't read from disk but contain - * the number of padding bytes so original size can be + * the number of padding bytes so original size can be * restored */ - if (na->ni->vol->efs_raw && - (na->data_flags & ATTR_IS_ENCRYPTED) && - ((pos + count) > max_init-2)) + if (na->ni->vol->efs_raw && + (na->data_flags & ATTR_IS_ENCRYPTED) && + ((pos + count) > max_init-2)) { return -35; //No encrypted files /* @@ -946,7 +210,7 @@ res_err_out: } */ } - + /* Find the runlist element containing the vcn. */ rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); if (!rl) { @@ -989,7 +253,7 @@ res_err_out: } if (rl->lcn < (LCN)0) { if (rl->lcn != (LCN)LCN_HOLE) { - ntfs_log_perror("%s: Bad run (%lld)", + ntfs_log_perror("%s: Bad run (%lld)", __FUNCTION__, (long long)rl->lcn); goto rl_err_out; @@ -1017,9 +281,10 @@ retry: */ br = to_read; // convert to sectors unit - u32 off_sec = b >> 9; - u32 sector = ((rl->lcn << vol->cluster_size_bits) + ofs) >> 9; - u32 count_sec = to_read >> 9; + u32 shift = size_to_shift(na->ni->vol->sector_size); + u32 off_sec = b >> shift; + u32 sector = ((rl->lcn << vol->cluster_size_bits) + ofs) >> shift; + u32 count_sec = to_read >> shift; int ret; ret = append_fragment(callback_data, off_sec, sector, count_sec); if (ret) { @@ -1102,4814 +367,3 @@ s64 ntfs_attr_getfragments(ntfs_attr *na, const s64 pos, s64 count, u64 offset, return ret; } -#if 0 - -static int ntfs_attr_fill_zero(ntfs_attr *na, s64 pos, s64 count) -{ - char *buf; - s64 written, size, end = pos + count; - s64 ofsi; - const runlist_element *rli; - ntfs_volume *vol; - int ret = -1; - - ntfs_log_trace("pos %lld, count %lld\n", (long long)pos, - (long long)count); - - if (!na || pos < 0 || count < 0) { - errno = EINVAL; - goto err_out; - } - - buf = ntfs_calloc(NTFS_BUF_SIZE); - if (!buf) - goto err_out; - - rli = na->rl; - ofsi = 0; - vol = na->ni->vol; - while (pos < end) { - while (rli->length && (ofsi + (rli->length << - vol->cluster_size_bits) <= pos)) { - ofsi += (rli->length << vol->cluster_size_bits); - rli++; - } - size = min(end - pos, NTFS_BUF_SIZE); - written = ntfs_rl_pwrite(vol, rli, ofsi, pos, size, buf); - if (written <= 0) { - ntfs_log_perror("Failed to zero space"); - goto err_free; - } - pos += written; - } - - ret = 0; -err_free: - free(buf); -err_out: - return ret; -} - -static int ntfs_attr_fill_hole(ntfs_attr *na, s64 count, s64 *ofs, - runlist_element **rl, VCN *update_from) -{ - s64 to_write; - s64 need; - ntfs_volume *vol = na->ni->vol; - int eo, ret = -1; - runlist *rlc; - LCN lcn_seek_from = -1; - VCN cur_vcn, from_vcn; - - to_write = min(count, ((*rl)->length << vol->cluster_size_bits) - *ofs); - - cur_vcn = (*rl)->vcn; - from_vcn = (*rl)->vcn + (*ofs >> vol->cluster_size_bits); - - ntfs_log_trace("count: %lld, cur_vcn: %lld, from: %lld, to: %lld, ofs: " - "%lld\n", (long long)count, (long long)cur_vcn, - (long long)from_vcn, (long long)to_write, (long long)*ofs); - - /* Map whole runlist to be able update mapping pairs later. */ - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; - - /* Restore @*rl, it probably get lost during runlist mapping. */ - *rl = ntfs_attr_find_vcn(na, cur_vcn); - if (!*rl) { - ntfs_log_error("Failed to find run after mapping runlist. " - "Please report to %s.\n", NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - - /* Search backwards to find the best lcn to start seek from. */ - rlc = *rl; - while (rlc->vcn) { - rlc--; - if (rlc->lcn >= 0) { - /* - * avoid fragmenting a compressed file - * Windows does not do that, and that may - * not be desirable for files which can - * be updated - */ - if (na->data_flags & ATTR_COMPRESSION_MASK) - lcn_seek_from = rlc->lcn + rlc->length; - else - lcn_seek_from = rlc->lcn + (from_vcn - rlc->vcn); - break; - } - } - if (lcn_seek_from == -1) { - /* Backwards search failed, search forwards. */ - rlc = *rl; - while (rlc->length) { - rlc++; - if (rlc->lcn >= 0) { - lcn_seek_from = rlc->lcn - (rlc->vcn - from_vcn); - if (lcn_seek_from < -1) - lcn_seek_from = -1; - break; - } - } - } - - need = ((*ofs + to_write - 1) >> vol->cluster_size_bits) - + 1 + (*rl)->vcn - from_vcn; - 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. - */ - rlc = ntfs_cluster_alloc(vol, (*rl)->vcn, (*rl)->length, - lcn_seek_from, DATA_ZONE); - } else - rlc = ntfs_cluster_alloc(vol, from_vcn, need, - lcn_seek_from, DATA_ZONE); - if (!rlc) - goto err_out; - - *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. - */ - if (*rl && (na->data_flags & ATTR_COMPRESSION_MASK)) - *rl = ntfs_rl_extend(*rl,1); - if (!*rl) { - eo = errno; - ntfs_log_perror("Failed to merge runlists"); - if (ntfs_cluster_free_from_rl(vol, rlc)) { - ntfs_log_perror("Failed to free hot clusters. " - "Please run chkdsk /f"); - } - errno = eo; - goto err_out; - } - na->rl = *rl; - if (*update_from == -1) - *update_from = from_vcn; - *rl = ntfs_attr_find_vcn(na, cur_vcn); - if (!*rl) { - /* - * It's definitely a BUG, if we failed to find @cur_vcn, because - * we missed it during instantiating of the hole. - */ - ntfs_log_error("Failed to find run after hole instantiation. " - "Please report to %s.\n", NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - /* If leaved part of the hole go to the next run. */ - if ((*rl)->lcn < 0) - (*rl)++; - /* Now LCN shoudn't be less than 0. */ - if ((*rl)->lcn < 0) { - ntfs_log_error("BUG! LCN is lesser than 0. " - "Please report to the %s.\n", NTFS_DEV_LIST); - errno = EIO; - goto err_out; - } - if (*ofs) { - /* Clear non-sparse region from @cur_vcn to @*ofs. */ - if (ntfs_attr_fill_zero(na, cur_vcn << vol->cluster_size_bits, - *ofs)) - goto err_out; - } - if ((*rl)->vcn < cur_vcn) { - /* - * Clusters that replaced hole are merged with - * previous run, so we need to update offset. - */ - *ofs += (cur_vcn - (*rl)->vcn) << vol->cluster_size_bits; - } - if ((*rl)->vcn > cur_vcn) { - /* - * We left part of the hole, so we need to update offset - */ - *ofs -= ((*rl)->vcn - cur_vcn) << vol->cluster_size_bits; - } - - ret = 0; -err_out: - return ret; -} - -static int stuff_hole(ntfs_attr *na, const s64 pos); - -/** - * ntfs_attr_pwrite - positioned write to an ntfs attribute - * @na: ntfs attribute to write to - * @pos: position in the attribute to write to - * @count: number of bytes to write - * @b: data buffer to write to disk - * - * This function will write @count bytes from data buffer @b to ntfs attribute - * @na at position @pos. - * - * On success, return the number of successfully written bytes. If this number - * is lower than @count this means that an error was encountered during the - * write so that the write is partial. 0 means nothing was written (also return - * 0 when @count is 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of ntfs_pwrite(), or to EINVAL in case of - * invalid arguments. - */ -s64 ntfs_attr_pwrite(ntfs_attr *na, const s64 pos, s64 count, const void *b) -{ - s64 written, to_write, ofs, old_initialized_size, old_data_size; - s64 total = 0; - VCN update_from = -1; - ntfs_volume *vol; - s64 fullcount; - ntfs_attr_search_ctx *ctx = NULL; - runlist_element *rl; - s64 hole_end; - int eo; - int compressed_part; - struct { - unsigned int undo_initialized_size : 1; - unsigned int undo_data_size : 1; - } need_to = { 0, 0 }; - BOOL makingnonresident = FALSE; - BOOL wasnonresident = FALSE; - BOOL compressed; - - 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, - (long long)pos, (long long)count); - - if (!na || !na->ni || !na->ni->vol || !b || pos < 0 || count < 0) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - goto errno_set; - } - vol = na->ni->vol; - compressed = (na->data_flags & ATTR_COMPRESSION_MASK) - != const_cpu_to_le16(0); - /* - * Encrypted attributes are only supported in raw mode. We return - * access denied, which is what Windows NT4 does, too. - * Moreover a file cannot be both encrypted and compressed. - */ - if ((na->data_flags & ATTR_IS_ENCRYPTED) - && (compressed || !vol->efs_raw)) { - errno = EACCES; - goto errno_set; - } - /* - * Fill the gap, when writing beyond the end of a compressed - * file. This will make recursive calls - */ - if (compressed - && (na->type == AT_DATA) - && (pos > na->initialized_size) - && stuff_hole(na,pos)) - 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. - */ - 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) - errno = EOPNOTSUPP; - goto errno_set; - } - - if (!count) - goto out; - /* for a compressed file, get prepared to reserve a full block */ - fullcount = count; - /* If the write reaches beyond the end, extend the attribute. */ - old_data_size = na->data_size; - if (pos + count > na->data_size) { - if (ntfs_attr_truncate(na, pos + count)) { - ntfs_log_perror("Failed to enlarge attribute"); - goto errno_set; - } - /* resizing may change the compression mode */ - compressed = (na->data_flags & ATTR_COMPRESSION_MASK) - != const_cpu_to_le16(0); - need_to.undo_data_size = 1; - } - /* - * For compressed data, a single full block was allocated - * to deal with compression, possibly in a previous call. - * We are not able to process several blocks because - * some clusters are freed after compression and - * new allocations have to be done before proceeding, - * so truncate the requested count if needed (big buffers). - */ - if (compressed) { - fullcount = na->data_size - pos; - if (count > fullcount) - count = fullcount; - } - old_initialized_size = na->initialized_size; - /* If it is a resident attribute, write the data to the mft record. */ - if (!NAttrNonResident(na)) { - char *val; - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto err_out; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) { - ntfs_log_perror("%s: lookup failed", __FUNCTION__); - goto err_out; - } - val = (char*)ctx->attr + le16_to_cpu(ctx->attr->value_offset); - if (val < (char*)ctx->attr || val + - le32_to_cpu(ctx->attr->value_length) > - (char*)ctx->mrec + vol->mft_record_size) { - errno = EIO; - ntfs_log_perror("%s: Sanity check failed", __FUNCTION__); - goto err_out; - } - memcpy(val + pos, b, count); - if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec)) { - /* - * NOTE: We are in a bad state at this moment. We have - * dirtied the mft record but we failed to commit it to - * disk. Since we have read the mft record ok before, - * it is unlikely to fail writing it, so is ok to just - * return error here... (AIA) - */ - ntfs_log_perror("%s: failed to write mft record", __FUNCTION__); - goto err_out; - } - ntfs_attr_put_search_ctx(ctx); - total = count; - goto out; - } - - /* Handle writes beyond initialized_size. */ - if (pos + count > na->initialized_size) { - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; - /* - * For a compressed attribute, we must be sure there is an - * available entry, and, when reopening a compressed file, - * we may need to split a hole. So reserve the entries - * before it gets too late. - */ - if (compressed) { - na->rl = ntfs_rl_extend(na->rl,2); - if (!na->rl) - goto err_out; - } - /* Set initialized_size to @pos + @count. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - goto err_out; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, - 0, NULL, 0, ctx)) - goto err_out; - - /* If write starts beyond initialized_size, zero the gap. */ - if (pos > na->initialized_size) - if (ntfs_attr_fill_zero(na, na->initialized_size, - pos - na->initialized_size)) - goto err_out; - - ctx->attr->initialized_size = cpu_to_sle64(pos + count); - /* fix data_size for compressed files */ - if (compressed) - ctx->attr->data_size = ctx->attr->initialized_size; - if (ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec)) { - /* - * Undo the change in the in-memory copy and send it - * back for writing. - */ - ctx->attr->initialized_size = - cpu_to_sle64(old_initialized_size); - ntfs_mft_record_write(vol, ctx->ntfs_ino->mft_no, - ctx->mrec); - goto err_out; - } - na->initialized_size = pos + count; - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - /* - * NOTE: At this point the initialized_size in the mft record - * has been updated BUT there is random data on disk thus if - * we decide to abort, we MUST change the initialized_size - * again. - */ - need_to.undo_initialized_size = 1; - } - /* Find the runlist element containing the vcn. */ - rl = ntfs_attr_find_vcn(na, pos >> vol->cluster_size_bits); - if (!rl) { - /* - * If the vcn is not present it is an out of bounds write. - * However, we already extended the size of the attribute, - * so getting this here must be an error of some kind. - */ - if (errno == ENOENT) { - errno = EIO; - ntfs_log_perror("%s: Failed to find VCN #3", __FUNCTION__); - } - 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). - * If so, decompression will be needed, and the full block - * must be allocated to be identified as uncompressed. - * This comes in two variants, depending on whether - * compression has saved at least one cluster. - * The compressed size can never be over full size by - * more than 485 (maximum for 15 compression blocks - * compressed to 4098 and the last 3640 bytes compressed - * to 3640 + 3640/8 = 4095, with 15*2 + 4095 - 3640 = 485) - * This is less than the smallest cluster, so the hole is - * is never beyond the cluster next to the position of - * the first uncompressed byte to write. - */ - compressed_part = 0; - if (compressed) { - if ((rl->lcn == (LCN)LCN_HOLE) - && wasnonresident) { - if (rl->length < na->compression_block_clusters) - 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++; - } - } - /* normal hole filling will do later */ - } else - if ((rl->lcn >= 0) && (rl[1].lcn == (LCN)LCN_HOLE)) { - s64 xofs; - - 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--; - } - } - /* - * 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) { - if (rl->lcn == LCN_RL_NOT_MAPPED) { - rl = ntfs_attr_find_vcn(na, rl->vcn); - if (!rl) { - if (errno == ENOENT) { - errno = EIO; - ntfs_log_perror("%s: Failed to find VCN" - " #4", __FUNCTION__); - } - goto rl_err_out; - } - /* Needed for case when runs merged. */ - ofs = pos + total - (rl->vcn << vol->cluster_size_bits); - } - if (!rl->length) { - errno = EIO; - ntfs_log_perror("%s: Zero run length", __FUNCTION__); - goto rl_err_out; - } - if (rl->lcn < (LCN)0) { - hole_end = rl->vcn + rl->length; - - if (rl->lcn != (LCN)LCN_HOLE) { - errno = EIO; - ntfs_log_perror("%s: Unexpected LCN (%lld)", - __FUNCTION__, - (long long)rl->lcn); - goto rl_err_out; - } - if (ntfs_attr_fill_hole(na, fullcount, &ofs, &rl, - &update_from)) - goto err_out; - } - if (compressed) { - while (rl->length - && (ofs >= (rl->length << vol->cluster_size_bits))) { - ofs -= rl->length << vol->cluster_size_bits; - rl++; - } - } - - /* It is a real lcn, write it to the volume. */ - to_write = min(count, (rl->length << vol->cluster_size_bits) - ofs); -retry: - ntfs_log_trace("Writing %lld bytes to vcn %lld, lcn %lld, ofs " - "%lld.\n", (long long)to_write, (long long)rl->vcn, - (long long)rl->lcn, (long long)ofs); - if (!NVolReadOnly(vol)) { - - s64 wpos = (rl->lcn << vol->cluster_size_bits) + ofs; - s64 wend = (rl->vcn << vol->cluster_size_bits) + ofs + to_write; - u32 bsize = vol->cluster_size; - /* Byte size needed to zero fill a cluster */ - s64 rounding = ((wend + bsize - 1) & ~(s64)(bsize - 1)) - wend; - /** - * Zero fill to cluster boundary if we're writing at the - * end of the attribute or into an ex-sparse cluster. - * This will cause the kernel not to seek and read disk - * blocks during write(2) to fill the end of the buffer - * which increases write speed by 2-10 fold typically. - * - * This is done even for compressed files, because - * data is generally first written uncompressed. - */ - if (rounding && ((wend == na->initialized_size) || - (wend < (hole_end << vol->cluster_size_bits)))){ - - char *cb; - - rounding += to_write; - - cb = ntfs_malloc(rounding); - if (!cb) - goto err_out; - - memcpy(cb, b, to_write); - memset(cb + to_write, 0, rounding - to_write); - - if (compressed) { - written = ntfs_compressed_pwrite(na, - rl, wpos, ofs, to_write, - rounding, b, compressed_part); - } else { - written = ntfs_pwrite(vol->dev, wpos, - rounding, cb); - if (written == rounding) - written = to_write; - } - - free(cb); - } else { - if (compressed) { - written = ntfs_compressed_pwrite(na, - rl, wpos, ofs, to_write, - to_write, b, compressed_part); - } else - written = ntfs_pwrite(vol->dev, wpos, - to_write, b); - } - } else - written = to_write; - /* If everything ok, update progress counters and continue. */ - if (written > 0) { - total += written; - count -= written; - fullcount -= written; - b = (const u8*)b + written; - } - if (written != to_write) { - /* Partial write cannot be dealt with, stop there */ - /* If the syscall was interrupted, try again. */ - if (written == (s64)-1 && errno == EINTR) - goto retry; - if (!written) - errno = EIO; - goto rl_err_out; - } - compressed_part = 0; - } -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*/)) { - /* - * FIXME: trying to recover by goto rl_err_out; - * could cause driver hang by infinite looping. - */ - total = -1; - goto out; - } -out: - ntfs_log_leave("\n"); - return total; -rl_err_out: - eo = errno; - if (total) { - if (need_to.undo_initialized_size) { - if (pos + total > na->initialized_size) - goto done; - /* - * TODO: Need to try to change initialized_size. If it - * succeeds goto done, otherwise goto err_out. (AIA) - */ - goto err_out; - } - goto done; - } - errno = eo; -err_out: - eo = errno; - if (need_to.undo_initialized_size) { - int err; - - err = 0; - if (!ctx) { - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - err = 1; - } else - ntfs_attr_reinit_search_ctx(ctx); - if (!err) { - err = ntfs_attr_lookup(na->type, na->name, - na->name_len, 0, 0, NULL, 0, ctx); - if (!err) { - na->initialized_size = old_initialized_size; - ctx->attr->initialized_size = cpu_to_sle64( - old_initialized_size); - err = ntfs_mft_record_write(vol, - ctx->ntfs_ino->mft_no, - ctx->mrec); - } - } - if (err) { - /* - * FIXME: At this stage could try to recover by filling - * old_initialized_size -> new_initialized_size with - * data or at least zeroes. (AIA) - */ - ntfs_log_error("Eeek! Failed to recover from error. " - "Leaving metadata in inconsistent " - "state! Run chkdsk!\n"); - } - } - 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*/); - /* 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"); - errno = eo; -errno_set: - total = -1; - goto out; -} - -int ntfs_attr_pclose(ntfs_attr *na) -{ - s64 written, ofs; - BOOL ok = TRUE; - VCN update_from = -1; - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx = NULL; - runlist_element *rl; - int eo; - s64 hole; - int compressed_part; - BOOL compressed; - - ntfs_log_enter("Entering for inode 0x%llx, attr 0x%x.\n", - na->ni->mft_no, na->type); - - if (!na || !na->ni || !na->ni->vol) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - goto errno_set; - } - vol = na->ni->vol; - compressed = (na->data_flags & ATTR_COMPRESSION_MASK) - != const_cpu_to_le16(0); - /* - * Encrypted non-resident attributes are not supported. We return - * access denied, which is what Windows NT4 does, too. - */ - if (NAttrEncrypted(na) && NAttrNonResident(na)) { - errno = EACCES; - goto errno_set; - } - /* If this is not a compressed attribute get out */ - /* same if it is resident */ - if (!compressed || !NAttrNonResident(na)) - goto out; - - /* - * For a compressed attribute, we must be sure there is an - * available entry, so reserve it before it gets too late. - */ - if (ntfs_attr_map_whole_runlist(na)) - goto err_out; - na->rl = ntfs_rl_extend(na->rl,1); - if (!na->rl) - goto err_out; - /* Find the runlist element containing the terminal vcn. */ - rl = ntfs_attr_find_vcn(na, (na->initialized_size - 1) >> vol->cluster_size_bits); - if (!rl) { - /* - * If the vcn is not present it is an out of bounds write. - * However, we have already written the last byte uncompressed, - * so getting this here must be an error of some kind. - */ - if (errno == ENOENT) { - errno = EIO; - ntfs_log_perror("%s: Failed to find VCN #5", __FUNCTION__); - } - goto err_out; - } - /* - * 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. - */ - 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 == (LCN)LCN_HOLE) { - if (rl->length < na->compression_block_clusters) - compressed_part - = na->compression_block_clusters - - rl->length; - else - compressed_part - = na->compression_block_clusters; - } - /* done, if the last block set was compressed */ - if (compressed_part) - goto out; - - ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); - - if (rl->lcn == LCN_RL_NOT_MAPPED) { - rl = ntfs_attr_find_vcn(na, rl->vcn); - if (!rl) { - if (errno == ENOENT) { - errno = EIO; - ntfs_log_perror("%s: Failed to find VCN" - " #6", __FUNCTION__); - } - goto rl_err_out; - } - /* Needed for case when runs merged. */ - ofs = na->initialized_size - (rl->vcn << vol->cluster_size_bits); - } - if (!rl->length) { - errno = EIO; - ntfs_log_perror("%s: Zero run length", __FUNCTION__); - goto rl_err_out; - } - if (rl->lcn < (LCN)0) { - hole = rl->vcn + rl->length; - if (rl->lcn != (LCN)LCN_HOLE) { - errno = EIO; - ntfs_log_perror("%s: Unexpected LCN (%lld)", - __FUNCTION__, - (long long)rl->lcn); - goto rl_err_out; - } - - if (ntfs_attr_fill_hole(na, (s64)0, &ofs, &rl, &update_from)) - goto err_out; - } - while (rl->length - && (ofs >= (rl->length << vol->cluster_size_bits))) { - ofs -= rl->length << vol->cluster_size_bits; - rl++; - } - -retry: - if (!NVolReadOnly(vol)) { - - written = ntfs_compressed_close(na, rl, ofs); - /* If everything ok, update progress counters and continue. */ - if (!written) - goto done; - } - /* 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 (ctx) - ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/)) { - /* - * FIXME: trying to recover by goto rl_err_out; - * could cause driver hang by infinite looping. - */ - ok = FALSE; - goto out; - } -out: - ntfs_log_leave("\n"); - return (!ok); -rl_err_out: - /* - * need not restore old sizes, only compressed_size - * can have changed. It has been set according to - * the current runlist while updating the mapping pairs, - * and must be kept consistent with the runlists. - */ -err_out: - eo = errno; - if (ctx) - ntfs_attr_put_search_ctx(ctx); - /* Update mapping pairs if needed. */ - ntfs_attr_update_mapping_pairs(na, 0 /*update_from*/); - errno = eo; -errno_set: - ok = FALSE; - goto out; -} - -/** - * ntfs_attr_mst_pread - multi sector transfer protected ntfs attribute read - * @na: multi sector transfer protected ntfs attribute to read from - * @pos: byte position in the attribute to begin reading from - * @bk_cnt: number of mst protected blocks to read - * @bk_size: size of each mst protected block in bytes - * @dst: output data buffer - * - * This function will read @bk_cnt blocks of size @bk_size bytes each starting - * at offset @pos from the ntfs attribute @na into the data buffer @b. - * - * On success, the multi sector transfer fixups are applied and the number of - * read blocks is returned. If this number is lower than @bk_cnt this means - * that the read has either reached end of attribute or that an error was - * encountered during the read so that the read is partial. 0 means end of - * attribute or nothing to read (also return 0 when @bk_cnt or @bk_size are 0). - * - * On error and nothing has been read, return -1 with errno set appropriately - * to the return code of ntfs_attr_pread() or to EINVAL in case of invalid - * arguments. - * - * NOTE: If an incomplete multi sector transfer is detected the magic is - * changed to BAAD but no error is returned, i.e. it is possible that any of - * the returned blocks have multi sector transfer errors. This should be - * detected by the caller by checking each block with is_baad_recordp(&block). - * The reasoning is that we want to fixup as many blocks as possible and we - * want to return even bad ones to the caller so, e.g. in case of ntfsck, the - * errors can be repaired. - */ -s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, - const u32 bk_size, void *dst) -{ - s64 br; - u8 *end; - - ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)pos); - if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - return -1; - } - br = ntfs_attr_pread(na, pos, bk_cnt * bk_size, dst); - if (br <= 0) - return br; - br /= bk_size; - for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + - bk_size) - ntfs_mst_post_read_fixup((NTFS_RECORD*)dst, bk_size); - /* Finally, return the number of blocks read. */ - return br; -} - -/** - * ntfs_attr_mst_pwrite - multi sector transfer protected ntfs attribute write - * @na: multi sector transfer protected ntfs attribute to write to - * @pos: position in the attribute to write to - * @bk_cnt: number of mst protected blocks to write - * @bk_size: size of each mst protected block in bytes - * @src: data buffer to write to disk - * - * This function will write @bk_cnt blocks of size @bk_size bytes each from - * data buffer @b to multi sector transfer (mst) protected ntfs attribute @na - * at position @pos. - * - * On success, return the number of successfully written blocks. If this number - * is lower than @bk_cnt this means that an error was encountered during the - * write so that the write is partial. 0 means nothing was written (also - * return 0 when @bk_cnt or @bk_size are 0). - * - * On error and nothing has been written, return -1 with errno set - * appropriately to the return code of ntfs_attr_pwrite(), or to EINVAL in case - * of invalid arguments. - * - * NOTE: We mst protect the data, write it, then mst deprotect it using a quick - * deprotect algorithm (no checking). This saves us from making a copy before - * the write and at the same time causes the usn to be incremented in the - * buffer. This conceptually fits in better with the idea that cached data is - * always deprotected and protection is performed when the data is actually - * going to hit the disk and the cache is immediately deprotected again - * simulating an mst read on the written data. This way cache coherency is - * achieved. - */ -s64 ntfs_attr_mst_pwrite(ntfs_attr *na, const s64 pos, s64 bk_cnt, - const u32 bk_size, void *src) -{ - s64 written, i; - - ntfs_log_trace("Entering for inode 0x%llx, attr type 0x%x, pos 0x%llx.\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)pos); - if (bk_cnt < 0 || bk_size % NTFS_BLOCK_SIZE) { - errno = EINVAL; - return -1; - } - if (!bk_cnt) - return 0; - /* Prepare data for writing. */ - for (i = 0; i < bk_cnt; ++i) { - int err; - - err = ntfs_mst_pre_write_fixup((NTFS_RECORD*) - ((u8*)src + i * bk_size), bk_size); - if (err < 0) { - /* Abort write at this position. */ - ntfs_log_perror("%s #1", __FUNCTION__); - if (!i) - return err; - bk_cnt = i; - break; - } - } - /* Write the prepared data. */ - written = ntfs_attr_pwrite(na, pos, bk_cnt * bk_size, src); - if (written <= 0) { - ntfs_log_perror("%s: written=%lld", __FUNCTION__, - (long long)written); - } - /* Quickly deprotect the data again. */ - for (i = 0; i < bk_cnt; ++i) - ntfs_mst_post_write_fixup((NTFS_RECORD*)((u8*)src + i * - bk_size)); - if (written <= 0) - return written; - /* Finally, return the number of complete blocks written. */ - return written / bk_size; -} - -/** - * ntfs_attr_find - find (next) attribute in mft record - * @type: attribute type to find - * @name: attribute name to find (optional, i.e. NULL means don't care) - * @name_len: attribute name length (only needed if @name present) - * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * You shouldn't need to call this function directly. Use lookup_attr() instead. - * - * ntfs_attr_find() takes a search context @ctx as parameter and searches the - * mft record specified by @ctx->mrec, beginning at @ctx->attr, for an - * attribute of @type, optionally @name and @val. If found, ntfs_attr_find() - * returns 0 and @ctx->attr will point to the found attribute. - * - * If not found, ntfs_attr_find() returns -1, with errno set to ENOENT and - * @ctx->attr will point to the attribute before which the attribute being - * searched for would need to be inserted if such an action were to be desired. - * - * On actual error, ntfs_attr_find() returns -1 with errno set to the error - * code but not to ENOENT. In this case @ctx->attr is undefined and in - * particular do not rely on it not changing. - * - * If @ctx->is_first is TRUE, the search begins with @ctx->attr itself. If it - * is FALSE, the search begins after @ctx->attr. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_find() repeatedly until it returns -1 with errno set to ENOENT to - * indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_find() will return the next attribute in the - * mft record @ctx->mrec. - * - * If @type is AT_END, seek to the end and return -1 with errno set to ENOENT. - * AT_END is not a valid attribute, its length is zero for example, thus it is - * safer to return error instead of success in this case. This also allows us - * to interoperate cleanly with ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * If @ic is IGNORE_CASE, the @name comparison is not case sensitive and - * @ctx->ntfs_ino must be set to the ntfs inode to which the mft record - * @ctx->mrec belongs. This is so we can get at the ntfs volume and hence at - * the upcase table. If @ic is CASE_SENSITIVE, the comparison is case - * sensitive. When @name is present, @name_len is the @name length in Unicode - * characters. - * - * If @name is not present (NULL), we assume that the unnamed attribute is - * being searched for. - * - * Finally, the resident attribute value @val is looked for, if present. - * If @val is not present (NULL), @val_len is ignored. - * - * ntfs_attr_find() only searches the specified mft record and it ignores the - * presence of an attribute list attribute (unless it is the one being searched - * for, obviously). If you need to take attribute lists into consideration, use - * ntfs_attr_lookup() instead (see below). This also means that you cannot use - * ntfs_attr_find() to search for extent records of non-resident attributes, as - * extents with lowest_vcn != 0 are usually described by the attribute list - * attribute only. - Note that it is possible that the first extent is only in - * the attribute list while the last extent is in the base mft record, so don't - * rely on being able to find the first extent in the base mft record. - * - * Warning: Never use @val when looking for attribute types which can be - * non-resident as this most likely will result in a crash! - */ -static int ntfs_attr_find(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const u8 *val, const u32 val_len, ntfs_attr_search_ctx *ctx) -{ - ATTR_RECORD *a; - ntfs_volume *vol; - ntfschar *upcase; - u32 upcase_len; - - ntfs_log_trace("attribute type 0x%x.\n", type); - - if (ctx->ntfs_ino) { - vol = ctx->ntfs_ino->vol; - upcase = vol->upcase; - upcase_len = vol->upcase_len; - } else { - if (name && name != AT_UNNAMED) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - return -1; - } - vol = NULL; - upcase = NULL; - upcase_len = 0; - } - /* - * Iterate over attributes in mft record starting at @ctx->attr, or the - * attribute following that, if @ctx->is_first is TRUE. - */ - if (ctx->is_first) { - a = ctx->attr; - ctx->is_first = FALSE; - } else - a = (ATTR_RECORD*)((char*)ctx->attr + - le32_to_cpu(ctx->attr->length)); - for (;; a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length))) { - if (p2n(a) < p2n(ctx->mrec) || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) - break; - ctx->attr = a; - if (((type != AT_UNUSED) && (le32_to_cpu(a->type) > - le32_to_cpu(type))) || - (a->type == AT_END)) { - errno = ENOENT; - return -1; - } - if (!a->length) - break; - /* If this is an enumeration return this attribute. */ - if (type == AT_UNUSED) - return 0; - if (a->type != type) - continue; - /* - * If @name is AT_UNNAMED we want an unnamed attribute. - * If @name is present, compare the two names. - * Otherwise, match any attribute. - */ - if (name == AT_UNNAMED) { - /* The search failed if the found attribute is named. */ - if (a->name_length) { - 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)) { - 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 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; - } - if (rc) - continue; - } - /* - * The names match or @name not present and attribute is - * unnamed. If no @val specified, we have found the attribute - * and are done. - */ - if (!val) - return 0; - /* @val is present; compare values. */ - else { - register int rc; - - rc = memcmp(val, (char*)a +le16_to_cpu(a->value_offset), - min(val_len, - le32_to_cpu(a->value_length))); - /* - * If @val collates before the current attribute's - * value, there is no matching attribute. - */ - if (!rc) { - register u32 avl; - avl = le32_to_cpu(a->value_length); - if (val_len == avl) - return 0; - if (val_len < avl) { - errno = ENOENT; - return -1; - } - } else if (rc < 0) { - errno = ENOENT; - return -1; - } - } - } - errno = EIO; - ntfs_log_perror("%s: Corrupt inode (%lld)", __FUNCTION__, - ctx->ntfs_ino ? (long long)ctx->ntfs_ino->mft_no : -1); - return -1; -} - -void ntfs_attr_name_free(char **name) -{ - if (*name) { - free(*name); - *name = NULL; - } -} - -char *ntfs_attr_name_get(const ntfschar *uname, const int uname_len) -{ - char *name = NULL; - int name_len; - - name_len = ntfs_ucstombs(uname, uname_len, &name, 0); - if (name_len < 0) { - ntfs_log_perror("ntfs_ucstombs"); - return NULL; - - } else if (name_len > 0) - return name; - - ntfs_attr_name_free(&name); - return NULL; -} - -/** - * ntfs_external_attr_find - find an attribute in the attribute list of an inode - * @type: attribute type to find - * @name: attribute name to find (optional, i.e. NULL means don't care) - * @name_len: attribute name length (only needed if @name present) - * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) - * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * You shouldn't need to call this function directly. Use ntfs_attr_lookup() - * instead. - * - * Find an attribute by searching the attribute list for the corresponding - * attribute list entry. Having found the entry, map the mft record for read - * if the attribute is in a different mft record/inode, find the attribute in - * there and return it. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_external_attr_find() repeatedly until it returns -1 with errno set to - * ENOENT to indicate that there are no more entries. During the enumeration, - * each successful call of ntfs_external_attr_find() will return the next - * attribute described by the attribute list of the base mft record described - * by the search context @ctx. - * - * If @type is AT_END, seek to the end of the base mft record ignoring the - * attribute list completely and return -1 with errno set to ENOENT. AT_END is - * not a valid attribute, its length is zero for example, thus it is safer to - * return error instead of success in this case. - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * On first search @ctx->ntfs_ino must be the inode of the base mft record and - * @ctx must have been obtained from a call to ntfs_attr_get_search_ctx(). - * On subsequent calls, @ctx->ntfs_ino can be any extent inode, too - * (@ctx->base_ntfs_ino is then the base inode). - * - * After finishing with the attribute/mft record you need to call - * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any - * mapped extent inodes, etc). - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * On success, @ctx->attr is the found attribute, it is in mft record - * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this - * attribute with @ctx->base_* being the base mft record to which @ctx->attr - * belongs. - * - * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the - * attribute which collates just after the attribute being searched for in the - * base ntfs inode, i.e. if one wants to add the attribute to the mft record - * this is the correct place to insert it into, and if there is not enough - * space, the attribute should be placed in an extent mft record. - * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list - * at which the new attribute's attribute list entry should be inserted. The - * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. - * The only exception to this is when @type is AT_END, in which case - * @ctx->al_entry is set to NULL also (see above). - * - * The following error codes are defined: - * ENOENT Attribute not found, not an error as such. - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - */ -static int ntfs_external_attr_find(ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - ntfs_inode *base_ni, *ni; - ntfs_volume *vol; - ATTR_LIST_ENTRY *al_entry, *next_al_entry; - u8 *al_start, *al_end; - ATTR_RECORD *a; - ntfschar *al_name; - u32 al_name_len; - BOOL is_first_search = FALSE; - - ni = ctx->ntfs_ino; - base_ni = ctx->base_ntfs_ino; - ntfs_log_trace("Entering for inode %lld, attribute type 0x%x.\n", - (unsigned long long)ni->mft_no, type); - if (!base_ni) { - /* First call happens with the base mft record. */ - base_ni = ctx->base_ntfs_ino = ctx->ntfs_ino; - ctx->base_mrec = ctx->mrec; - } - if (ni == base_ni) - ctx->base_attr = ctx->attr; - if (type == AT_END) - goto not_found; - vol = base_ni->vol; - al_start = base_ni->attr_list; - al_end = al_start + base_ni->attr_list_size; - if (!ctx->al_entry) { - ctx->al_entry = (ATTR_LIST_ENTRY*)al_start; - is_first_search = TRUE; - } - /* - * Iterate over entries in attribute list starting at @ctx->al_entry, - * or the entry following that, if @ctx->is_first is TRUE. - */ - if (ctx->is_first) { - al_entry = ctx->al_entry; - ctx->is_first = FALSE; - /* - * If an enumeration and the first attribute is higher than - * the attribute list itself, need to return the attribute list - * attribute. - */ - if ((type == AT_UNUSED) && is_first_search && - le32_to_cpu(al_entry->type) > - le32_to_cpu(AT_ATTRIBUTE_LIST)) - goto find_attr_list_attr; - } else { - al_entry = (ATTR_LIST_ENTRY*)((char*)ctx->al_entry + - le16_to_cpu(ctx->al_entry->length)); - /* - * If this is an enumeration and the attribute list attribute - * is the next one in the enumeration sequence, just return the - * attribute list attribute from the base mft record as it is - * not listed in the attribute list itself. - */ - if ((type == AT_UNUSED) && le32_to_cpu(ctx->al_entry->type) < - le32_to_cpu(AT_ATTRIBUTE_LIST) && - le32_to_cpu(al_entry->type) > - le32_to_cpu(AT_ATTRIBUTE_LIST)) { - int rc; -find_attr_list_attr: - - /* Check for bogus calls. */ - if (name || name_len || val || val_len || lowest_vcn) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - return -1; - } - - /* We want the base record. */ - ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - ctx->is_first = TRUE; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - - /* Find the attribute list attribute. */ - rc = ntfs_attr_find(AT_ATTRIBUTE_LIST, NULL, 0, - IGNORE_CASE, NULL, 0, ctx); - - /* - * Setup the search context so the correct - * attribute is returned next time round. - */ - ctx->al_entry = al_entry; - ctx->is_first = TRUE; - - /* Got it. Done. */ - if (!rc) - return 0; - - /* Error! If other than not found return it. */ - if (errno != ENOENT) - return rc; - - /* Not found?!? Absurd! */ - errno = EIO; - ntfs_log_error("Attribute list wasn't found"); - return -1; - } - } - for (;; al_entry = next_al_entry) { - /* Out of bounds check. */ - if ((u8*)al_entry < base_ni->attr_list || - (u8*)al_entry > al_end) - break; /* Inode is corrupt. */ - ctx->al_entry = al_entry; - /* Catch the end of the attribute list. */ - if ((u8*)al_entry == al_end) - goto not_found; - if (!al_entry->length) - break; - if ((u8*)al_entry + 6 > al_end || (u8*)al_entry + - le16_to_cpu(al_entry->length) > al_end) - break; - next_al_entry = (ATTR_LIST_ENTRY*)((u8*)al_entry + - le16_to_cpu(al_entry->length)); - if (type != AT_UNUSED) { - if (le32_to_cpu(al_entry->type) > le32_to_cpu(type)) - goto not_found; - if (type != al_entry->type) - continue; - } - al_name_len = al_entry->name_length; - al_name = (ntfschar*)((u8*)al_entry + al_entry->name_offset); - /* - * If !@type we want the attribute represented by this - * attribute list entry. - */ - if (type == AT_UNUSED) - goto is_enumeration; - /* - * If @name is AT_UNNAMED we want an unnamed attribute. - * If @name is present, compare the two names. - * Otherwise, match any attribute. - */ - 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; - - 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) - continue; - } - /* - * The names match or @name not present and attribute is - * unnamed. Now check @lowest_vcn. Continue search if the - * next attribute list entry still fits @lowest_vcn. Otherwise - * we have reached the right one or the search has failed. - */ - if (lowest_vcn && (u8*)next_al_entry >= al_start && - (u8*)next_al_entry + 6 < al_end && - (u8*)next_al_entry + le16_to_cpu( - next_al_entry->length) <= al_end && - sle64_to_cpu(next_al_entry->lowest_vcn) <= - lowest_vcn && - next_al_entry->type == al_entry->type && - next_al_entry->name_length == al_name_len && - ntfs_names_are_equal((ntfschar*)((char*) - next_al_entry + - next_al_entry->name_offset), - next_al_entry->name_length, - al_name, al_name_len, CASE_SENSITIVE, - vol->upcase, vol->upcase_len)) - continue; -is_enumeration: - if (MREF_LE(al_entry->mft_reference) == ni->mft_no) { - if (MSEQNO_LE(al_entry->mft_reference) != - le16_to_cpu( - ni->mrec->sequence_number)) { - ntfs_log_error("Found stale mft reference in " - "attribute list!\n"); - break; - } - } else { /* Mft references do not match. */ - /* Do we want the base record back? */ - if (MREF_LE(al_entry->mft_reference) == - base_ni->mft_no) { - ni = ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - } else { - /* We want an extent record. */ - ni = ntfs_extent_inode_open(base_ni, - al_entry->mft_reference); - if (!ni) - break; - ctx->ntfs_ino = ni; - ctx->mrec = ni->mrec; - } - } - a = ctx->attr = (ATTR_RECORD*)((char*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - /* - * ctx->ntfs_ino, ctx->mrec, and ctx->attr now point to the - * mft record containing the attribute represented by the - * current al_entry. - * - * We could call into ntfs_attr_find() to find the right - * attribute in this mft record but this would be less - * efficient and not quite accurate as ntfs_attr_find() ignores - * the attribute instance numbers for example which become - * important when one plays with attribute lists. Also, because - * a proper match has been found in the attribute list entry - * above, the comparison can now be optimized. So it is worth - * re-implementing a simplified ntfs_attr_find() here. - * - * Use a manual loop so we can still use break and continue - * with the same meanings as above. - */ -do_next_attr_loop: - if ((char*)a < (char*)ctx->mrec || (char*)a > (char*)ctx->mrec + - le32_to_cpu(ctx->mrec->bytes_allocated)) - break; - if (a->type == AT_END) - continue; - if (!a->length) - break; - if (al_entry->instance != a->instance) - goto do_next_attr; - /* - * If the type and/or the name are/is mismatched between the - * attribute list entry and the attribute record, there is - * corruption so we break and return error EIO. - */ - if (al_entry->type != a->type) - break; - if (!ntfs_names_are_equal((ntfschar*)((char*)a + - le16_to_cpu(a->name_offset)), - a->name_length, al_name, - al_name_len, CASE_SENSITIVE, - vol->upcase, vol->upcase_len)) - break; - ctx->attr = a; - /* - * If no @val specified or @val specified and it matches, we - * have found it! Also, if !@type, it is an enumeration, so we - * want the current attribute. - */ - if ((type == AT_UNUSED) || !val || (!a->non_resident && - le32_to_cpu(a->value_length) == val_len && - !memcmp((char*)a + le16_to_cpu(a->value_offset), - val, val_len))) { - return 0; - } -do_next_attr: - /* Proceed to the next attribute in the current mft record. */ - a = (ATTR_RECORD*)((char*)a + le32_to_cpu(a->length)); - goto do_next_attr_loop; - } - if (ni != base_ni) { - ctx->ntfs_ino = base_ni; - ctx->mrec = ctx->base_mrec; - ctx->attr = ctx->base_attr; - } - errno = EIO; - ntfs_log_perror("Inode is corrupt (%lld)", (long long)base_ni->mft_no); - return -1; -not_found: - /* - * If we were looking for AT_END or we were enumerating and reached the - * end, we reset the search context @ctx and use ntfs_attr_find() to - * seek to the end of the base mft record. - */ - if (type == AT_UNUSED || type == AT_END) { - ntfs_attr_reinit_search_ctx(ctx); - return ntfs_attr_find(AT_END, name, name_len, ic, val, val_len, - ctx); - } - /* - * The attribute wasn't found. Before we return, we want to ensure - * @ctx->mrec and @ctx->attr indicate the position at which the - * attribute should be inserted in the base mft record. Since we also - * want to preserve @ctx->al_entry we cannot reinitialize the search - * context using ntfs_attr_reinit_search_ctx() as this would set - * @ctx->al_entry to NULL. Thus we do the necessary bits manually (see - * ntfs_attr_init_search_ctx() below). Note, we _only_ preserve - * @ctx->al_entry as the remaining fields (base_*) are identical to - * their non base_ counterparts and we cannot set @ctx->base_attr - * correctly yet as we do not know what @ctx->attr will be set to by - * the call to ntfs_attr_find() below. - */ - ctx->mrec = ctx->base_mrec; - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - ctx->is_first = TRUE; - ctx->ntfs_ino = ctx->base_ntfs_ino; - ctx->base_ntfs_ino = NULL; - ctx->base_mrec = NULL; - ctx->base_attr = NULL; - /* - * In case there are multiple matches in the base mft record, need to - * keep enumerating until we get an attribute not found response (or - * another error), otherwise we would keep returning the same attribute - * over and over again and all programs using us for enumeration would - * lock up in a tight loop. - */ - { - int ret; - - do { - ret = ntfs_attr_find(type, name, name_len, ic, val, - val_len, ctx); - } while (!ret); - return ret; - } -} - -/** - * ntfs_attr_lookup - find an attribute in an ntfs inode - * @type: attribute type to find - * @name: attribute name to find (optional, i.e. NULL means don't care) - * @name_len: attribute name length (only needed if @name present) - * @ic: IGNORE_CASE or CASE_SENSITIVE (ignored if @name not present) - * @lowest_vcn: lowest vcn to find (optional, non-resident attributes only) - * @val: attribute value to find (optional, resident attributes only) - * @val_len: attribute value length - * @ctx: search context with mft record and attribute to search from - * - * Find an attribute in an ntfs inode. On first search @ctx->ntfs_ino must - * be the base mft record and @ctx must have been obtained from a call to - * ntfs_attr_get_search_ctx(). - * - * This function transparently handles attribute lists and @ctx is used to - * continue searches where they were left off at. - * - * If @type is AT_UNUSED, return the first found attribute, i.e. one can - * enumerate all attributes by setting @type to AT_UNUSED and then calling - * ntfs_attr_lookup() repeatedly until it returns -1 with errno set to ENOENT - * to indicate that there are no more entries. During the enumeration, each - * successful call of ntfs_attr_lookup() will return the next attribute, with - * the current attribute being described by the search context @ctx. - * - * If @type is AT_END, seek to the end of the base mft record ignoring the - * attribute list completely and return -1 with errno set to ENOENT. AT_END is - * not a valid attribute, its length is zero for example, thus it is safer to - * return error instead of success in this case. It should never be needed to - * do this, but we implement the functionality because it allows for simpler - * code inside ntfs_external_attr_find(). - * - * If @name is AT_UNNAMED search for an unnamed attribute. If @name is present - * but not AT_UNNAMED search for a named attribute matching @name. Otherwise, - * match both named and unnamed attributes. - * - * After finishing with the attribute/mft record you need to call - * ntfs_attr_put_search_ctx() to cleanup the search context (unmapping any - * mapped extent inodes, etc). - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * On success, @ctx->attr is the found attribute, it is in mft record - * @ctx->mrec, and @ctx->al_entry is the attribute list entry for this - * attribute with @ctx->base_* being the base mft record to which @ctx->attr - * belongs. If no attribute list attribute is present @ctx->al_entry and - * @ctx->base_* are NULL. - * - * On error ENOENT, i.e. attribute not found, @ctx->attr is set to the - * attribute which collates just after the attribute being searched for in the - * base ntfs inode, i.e. if one wants to add the attribute to the mft record - * this is the correct place to insert it into, and if there is not enough - * space, the attribute should be placed in an extent mft record. - * @ctx->al_entry points to the position within @ctx->base_ntfs_ino->attr_list - * at which the new attribute's attribute list entry should be inserted. The - * other @ctx fields, base_ntfs_ino, base_mrec, and base_attr are set to NULL. - * The only exception to this is when @type is AT_END, in which case - * @ctx->al_entry is set to NULL also (see above). - * - * - * The following error codes are defined: - * ENOENT Attribute not found, not an error as such. - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - */ -int ntfs_attr_lookup(const ATTR_TYPES type, const ntfschar *name, - const u32 name_len, const IGNORE_CASE_BOOL ic, - const VCN lowest_vcn, const u8 *val, const u32 val_len, - ntfs_attr_search_ctx *ctx) -{ - ntfs_volume *vol; - ntfs_inode *base_ni; - int ret = -1; - - ntfs_log_enter("Entering for attribute type 0x%x\n", type); - - if (!ctx || !ctx->mrec || !ctx->attr || (name && name != AT_UNNAMED && - (!ctx->ntfs_ino || !(vol = ctx->ntfs_ino->vol) || - !vol->upcase || !vol->upcase_len))) { - errno = EINVAL; - ntfs_log_perror("%s", __FUNCTION__); - goto out; - } - - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - if (!base_ni || !NInoAttrList(base_ni) || type == AT_ATTRIBUTE_LIST) - ret = ntfs_attr_find(type, name, name_len, ic, val, val_len, ctx); - else - ret = ntfs_external_attr_find(type, name, name_len, ic, - lowest_vcn, val, val_len, ctx); -out: - ntfs_log_leave("\n"); - return ret; -} - -/** - * ntfs_attr_position - find given or next attribute type in an ntfs inode - * @type: attribute type to start lookup - * @ctx: search context with mft record and attribute to search from - * - * Find an attribute type in an ntfs inode or the next attribute which is not - * the AT_END attribute. Please see more details at ntfs_attr_lookup. - * - * Return 0 if the search was successful and -1 if not, with errno set to the - * error code. - * - * The following error codes are defined: - * EINVAL Invalid arguments. - * EIO I/O error or corrupt data structures found. - * ENOMEM Not enough memory to allocate necessary buffers. - * ENOSPC No attribute was found after 'type', only AT_END. - */ -int ntfs_attr_position(const ATTR_TYPES type, ntfs_attr_search_ctx *ctx) -{ - if (ntfs_attr_lookup(type, NULL, 0, CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (errno != ENOENT) - return -1; - if (ctx->attr->type == AT_END) { - errno = ENOSPC; - return -1; - } - } - return 0; -} - -/** - * ntfs_attr_init_search_ctx - initialize an attribute search context - * @ctx: attribute search context to initialize - * @ni: ntfs inode with which to initialize the search context - * @mrec: mft record with which to initialize the search context - * - * Initialize the attribute search context @ctx with @ni and @mrec. - */ -static void ntfs_attr_init_search_ctx(ntfs_attr_search_ctx *ctx, - ntfs_inode *ni, MFT_RECORD *mrec) -{ - if (!mrec) - mrec = ni->mrec; - ctx->mrec = mrec; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)mrec + le16_to_cpu(mrec->attrs_offset)); - ctx->is_first = TRUE; - ctx->ntfs_ino = ni; - ctx->al_entry = NULL; - ctx->base_ntfs_ino = NULL; - ctx->base_mrec = NULL; - ctx->base_attr = NULL; -} - -/** - * ntfs_attr_reinit_search_ctx - reinitialize an attribute search context - * @ctx: attribute search context to reinitialize - * - * Reinitialize the attribute search context @ctx. - * - * This is used when a search for a new attribute is being started to reset - * the search context to the beginning. - */ -void ntfs_attr_reinit_search_ctx(ntfs_attr_search_ctx *ctx) -{ - if (!ctx->base_ntfs_ino) { - /* No attribute list. */ - ctx->is_first = TRUE; - /* Sanity checks are performed elsewhere. */ - ctx->attr = (ATTR_RECORD*)((u8*)ctx->mrec + - le16_to_cpu(ctx->mrec->attrs_offset)); - /* - * This needs resetting due to ntfs_external_attr_find() which - * can leave it set despite having zeroed ctx->base_ntfs_ino. - */ - ctx->al_entry = NULL; - return; - } /* Attribute list. */ - ntfs_attr_init_search_ctx(ctx, ctx->base_ntfs_ino, ctx->base_mrec); - return; -} - -/** - * ntfs_attr_get_search_ctx - allocate/initialize a new attribute search context - * @ni: ntfs inode with which to initialize the search context - * @mrec: mft record with which to initialize the search context - * - * Allocate a new attribute search context, initialize it with @ni and @mrec, - * and return it. Return NULL on error with errno set. - * - * @mrec can be NULL, in which case the mft record is taken from @ni. - * - * Note: For low level utilities which know what they are doing we allow @ni to - * be NULL and @mrec to be set. Do NOT do this unless you understand the - * implications!!! For example it is no longer safe to call ntfs_attr_lookup(). - */ -ntfs_attr_search_ctx *ntfs_attr_get_search_ctx(ntfs_inode *ni, MFT_RECORD *mrec) -{ - ntfs_attr_search_ctx *ctx; - - if (!ni && !mrec) { - errno = EINVAL; - ntfs_log_perror("NULL arguments"); - return NULL; - } - ctx = ntfs_malloc(sizeof(ntfs_attr_search_ctx)); - if (ctx) - ntfs_attr_init_search_ctx(ctx, ni, mrec); - return ctx; -} - -/** - * ntfs_attr_put_search_ctx - release an attribute search context - * @ctx: attribute search context to free - * - * Release the attribute search context @ctx. - */ -void ntfs_attr_put_search_ctx(ntfs_attr_search_ctx *ctx) -{ - // NOTE: save errno if it could change and function stays void! - free(ctx); -} - -/** - * ntfs_attr_find_in_attrdef - find an attribute in the $AttrDef system file - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to find - * - * Search for the attribute definition record corresponding to the attribute - * @type in the $AttrDef system file. - * - * Return the attribute type definition record if found and NULL if not found - * or an error occurred. On error the error code is stored in errno. The - * following error codes are defined: - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - */ -ATTR_DEF *ntfs_attr_find_in_attrdef(const ntfs_volume *vol, - const ATTR_TYPES type) -{ - ATTR_DEF *ad; - - if (!vol || !vol->attrdef || !type) { - errno = EINVAL; - ntfs_log_perror("%s: type=%d", __FUNCTION__, type); - return NULL; - } - for (ad = vol->attrdef; (u8*)ad - (u8*)vol->attrdef < - vol->attrdef_len && ad->type; ++ad) { - /* We haven't found it yet, carry on searching. */ - if (le32_to_cpu(ad->type) < le32_to_cpu(type)) - continue; - /* We found the attribute; return it. */ - if (ad->type == type) - return ad; - /* We have gone too far already. No point in continuing. */ - break; - } - errno = ENOENT; - ntfs_log_perror("%s: type=%d", __FUNCTION__, type); - return NULL; -} - -/** - * ntfs_attr_size_bounds_check - check a size of an attribute type for validity - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * @size: size which to check - * - * Check whether the @size in bytes is valid for an attribute of @type on the - * ntfs volume @vol. This information is obtained from $AttrDef system file. - * - * Return 0 if valid and -1 if not valid or an error occurred. On error the - * error code is stored in errno. The following error codes are defined: - * ERANGE - @size is not valid for the attribute @type. - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @size is < 0 or @vol is not valid). - */ -int ntfs_attr_size_bounds_check(const ntfs_volume *vol, const ATTR_TYPES type, - const s64 size) -{ - ATTR_DEF *ad; - s64 min_size, max_size; - - if (size < 0) { - errno = EINVAL; - ntfs_log_perror("%s: size=%lld", __FUNCTION__, - (long long)size); - return -1; - } - - /* - * $ATTRIBUTE_LIST shouldn't be greater than 0x40000, otherwise - * Windows would crash. This is not listed in the AttrDef. - */ - if (type == AT_ATTRIBUTE_LIST && size > 0x40000) { - errno = ERANGE; - ntfs_log_perror("Too large attrlist (%lld)", (long long)size); - return -1; - } - - ad = ntfs_attr_find_in_attrdef(vol, type); - if (!ad) - return -1; - - min_size = sle64_to_cpu(ad->min_size); - max_size = sle64_to_cpu(ad->max_size); - - if ((min_size && (size < min_size)) || - ((max_size > 0) && (size > max_size))) { - errno = ERANGE; - ntfs_log_perror("Attr type %d size check failed (min,size,max=" - "%lld,%lld,%lld)", type, (long long)min_size, - (long long)size, (long long)max_size); - return -1; - } - return 0; -} - -/** - * ntfs_attr_can_be_non_resident - check if an attribute can be non-resident - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * - * Check whether the attribute of @type on the ntfs volume @vol is allowed to - * be non-resident. This information is obtained from $AttrDef system file. - * - * Return 0 if the attribute is allowed to be non-resident and -1 if not or an - * error occurred. On error the error code is stored in errno. The following - * error codes are defined: - * EPERM - The attribute is not allowed to be non-resident. - * ENOENT - The attribute @type is not specified in $AttrDef. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - */ -int ntfs_attr_can_be_non_resident(const ntfs_volume *vol, const ATTR_TYPES type) -{ - ATTR_DEF *ad; - - /* Find the attribute definition record in $AttrDef. */ - ad = ntfs_attr_find_in_attrdef(vol, type); - if (!ad) - return -1; - /* Check the flags and return the result. */ - if (ad->flags & ATTR_DEF_RESIDENT) { - errno = EPERM; - ntfs_log_trace("Attribute can't be non-resident\n"); - return -1; - } - return 0; -} - -/** - * ntfs_attr_can_be_resident - check if an attribute can be resident - * @vol: ntfs volume to which the attribute belongs - * @type: attribute type which to check - * - * Check whether the attribute of @type on the ntfs volume @vol is allowed to - * be resident. This information is derived from our ntfs knowledge and may - * not be completely accurate, especially when user defined attributes are - * present. Basically we allow everything to be resident except for index - * allocation and extended attribute attributes. - * - * Return 0 if the attribute is allowed to be resident and -1 if not or an - * error occurred. On error the error code is stored in errno. The following - * error codes are defined: - * EPERM - The attribute is not allowed to be resident. - * EINVAL - Invalid parameters (e.g. @vol is not valid). - * - * Warning: In the system file $MFT the attribute $Bitmap must be non-resident - * otherwise windows will not boot (blue screen of death)! We cannot - * check for this here as we don't know which inode's $Bitmap is being - * asked about so the caller needs to special case this. - */ -int ntfs_attr_can_be_resident(const ntfs_volume *vol, const ATTR_TYPES type) -{ - if (!vol || !vol->attrdef || !type) { - errno = EINVAL; - return -1; - } - if (type != AT_INDEX_ALLOCATION) - return 0; - - ntfs_log_trace("Attribute can't be resident\n"); - errno = EPERM; - return -1; -} - -/** - * ntfs_make_room_for_attr - make room for an attribute inside an mft record - * @m: mft record - * @pos: position at which to make space - * @size: byte size to make available at this position - * - * @pos points to the attribute in front of which we want to make space. - * - * Return 0 on success or -1 on error. On error the error code is stored in - * errno. Possible error codes are: - * ENOSPC - There is not enough space available to complete operation. The - * caller has to make space before calling this. - * EINVAL - Input parameters were faulty. - */ -int ntfs_make_room_for_attr(MFT_RECORD *m, u8 *pos, u32 size) -{ - u32 biu; - - ntfs_log_trace("Entering for pos 0x%d, size %u.\n", - (int)(pos - (u8*)m), (unsigned) size); - - /* Make size 8-byte alignment. */ - size = (size + 7) & ~7; - - /* Rigorous consistency checks. */ - if (!m || !pos || pos < (u8*)m) { - errno = EINVAL; - ntfs_log_perror("%s: pos=%p m=%p", __FUNCTION__, pos, m); - return -1; - } - /* The -8 is for the attribute terminator. */ - if (pos - (u8*)m > (int)le32_to_cpu(m->bytes_in_use) - 8) { - errno = EINVAL; - return -1; - } - /* Nothing to do. */ - if (!size) - return 0; - - biu = le32_to_cpu(m->bytes_in_use); - /* Do we have enough space? */ - if (biu + size > le32_to_cpu(m->bytes_allocated) || - pos + size > (u8*)m + le32_to_cpu(m->bytes_allocated)) { - errno = ENOSPC; - ntfs_log_trace("No enough space in the MFT record\n"); - return -1; - } - /* Move everything after pos to pos + size. */ - memmove(pos + size, pos, biu - (pos - (u8*)m)); - /* Update mft record. */ - m->bytes_in_use = cpu_to_le32(biu + size); - return 0; -} - -/** - * ntfs_resident_attr_record_add - add resident attribute to inode - * @ni: opened ntfs inode to which MFT record add attribute - * @type: type of the new attribute - * @name: name of the new attribute - * @name_len: name length of the new attribute - * @val: value of the new attribute - * @size: size of new attribute (length of @val, if @val != NULL) - * @flags: flags of the new attribute - * - * Return offset to attribute from the beginning of the mft record on success - * and -1 on error. On error the error code is stored in errno. - * Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EEXIST - Attribute of such type and with same name already exists. - * EIO - I/O error occurred or damaged filesystem. - */ -int ntfs_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, u32 size, - ATTR_FLAGS data_flags) -{ - ntfs_attr_search_ctx *ctx; - u32 length; - ATTR_RECORD *a; - MFT_RECORD *m; - int err, offset; - ntfs_inode *base_ni; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, flags 0x%x.\n", - (long long) ni->mft_no, (unsigned) type, (unsigned) data_flags); - - if (!ni || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_resident(ni->vol, type)) { - if (errno == EPERM) - ntfs_log_trace("Attribute can't be resident.\n"); - else - ntfs_log_trace("ntfs_attr_can_be_resident failed.\n"); - return -1; - } - - /* Locate place where record should be. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return -1; - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, val, size, - ctx)) { - err = EEXIST; - ntfs_log_trace("Attribute already present.\n"); - goto put_err_out; - } - if (errno != ENOENT) { - err = EIO; - goto put_err_out; - } - a = ctx->attr; - m = ctx->mrec; - - /* Make room for attribute. */ - length = offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + - ((size + 7) & ~7); - if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { - err = errno; - ntfs_log_trace("Failed to make room for attribute.\n"); - goto put_err_out; - } - - /* Setup record fields. */ - offset = ((u8*)a - (u8*)m); - a->type = type; - a->length = cpu_to_le32(length); - a->non_resident = 0; - a->name_length = name_len; - a->name_offset = (name_len - ? cpu_to_le16(offsetof(ATTR_RECORD, resident_end)) - : const_cpu_to_le16(0)); - a->flags = data_flags; - a->instance = m->next_attr_instance; - a->value_length = cpu_to_le32(size); - a->value_offset = cpu_to_le16(length - ((size + 7) & ~7)); - if (val) - memcpy((u8*)a + le16_to_cpu(a->value_offset), val, size); - else - memset((u8*)a + le16_to_cpu(a->value_offset), 0, size); - if (type == AT_FILE_NAME) - a->resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->resident_flags = 0; - if (name_len) - memcpy((u8*)a + le16_to_cpu(a->name_offset), - name, sizeof(ntfschar) * name_len); - m->next_attr_instance = - cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - if (ni->nr_extents == -1) - base_ni = ni->base_ni; - else - base_ni = ni; - if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { - if (ntfs_attrlist_entry_add(ni, a)) { - err = errno; - ntfs_attr_record_resize(m, a, 0); - ntfs_log_trace("Failed add attribute entry to " - "ATTRIBUTE_LIST.\n"); - goto put_err_out; - } - } - if (type == AT_DATA && name == AT_UNNAMED) { - ni->data_size = size; - ni->allocated_size = (size + 7) & ~7; - } - ntfs_inode_mark_dirty(ni); - ntfs_attr_put_search_ctx(ctx); - return offset; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_non_resident_attr_record_add - add extent of non-resident attribute - * @ni: opened ntfs inode to which MFT record add attribute - * @type: type of the new attribute extent - * @name: name of the new attribute extent - * @name_len: name length of the new attribute extent - * @lowest_vcn: lowest vcn of the new attribute extent - * @dataruns_size: dataruns size of the new attribute extent - * @flags: flags of the new attribute extent - * - * Return offset to attribute from the beginning of the mft record on success - * and -1 on error. On error the error code is stored in errno. - * Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EEXIST - Attribute of such type, with same lowest vcn and with same - * name already exists. - * EIO - I/O error occurred or damaged filesystem. - */ -int ntfs_non_resident_attr_record_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, VCN lowest_vcn, int dataruns_size, - ATTR_FLAGS flags) -{ - ntfs_attr_search_ctx *ctx; - u32 length; - ATTR_RECORD *a; - MFT_RECORD *m; - ntfs_inode *base_ni; - int err, offset; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x, lowest_vcn %lld, " - "dataruns_size %d, flags 0x%x.\n", - (long long) ni->mft_no, (unsigned) type, - (long long) lowest_vcn, dataruns_size, (unsigned) flags); - - if (!ni || dataruns_size <= 0 || (!name && name_len)) { - errno = EINVAL; - return -1; - } - - if (ntfs_attr_can_be_non_resident(ni->vol, type)) { - if (errno == EPERM) - ntfs_log_perror("Attribute can't be non resident"); - else - ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); - return -1; - } - - /* Locate place where record should be. */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return -1; - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(type, name, name_len, CASE_SENSITIVE, NULL, 0, - ctx)) { - err = EEXIST; - ntfs_log_perror("Attribute 0x%x already present", type); - goto put_err_out; - } - if (errno != ENOENT) { - ntfs_log_perror("ntfs_attr_find failed"); - err = EIO; - goto put_err_out; - } - a = ctx->attr; - m = ctx->mrec; - - /* Make room for attribute. */ - dataruns_size = (dataruns_size + 7) & ~7; - length = offsetof(ATTR_RECORD, compressed_size) + ((sizeof(ntfschar) * - name_len + 7) & ~7) + dataruns_size + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->compressed_size) : 0); - if (ntfs_make_room_for_attr(ctx->mrec, (u8*) ctx->attr, length)) { - err = errno; - ntfs_log_perror("Failed to make room for attribute"); - goto put_err_out; - } - - /* Setup record fields. */ - a->type = type; - a->length = cpu_to_le32(length); - a->non_resident = 1; - a->name_length = name_len; - a->name_offset = cpu_to_le16(offsetof(ATTR_RECORD, compressed_size) + - ((flags & (ATTR_IS_COMPRESSED | ATTR_IS_SPARSE)) ? - sizeof(a->compressed_size) : 0)); - a->flags = flags; - a->instance = m->next_attr_instance; - a->lowest_vcn = cpu_to_sle64(lowest_vcn); - a->mapping_pairs_offset = cpu_to_le16(length - dataruns_size); - a->compression_unit = (flags & ATTR_IS_COMPRESSED) - ? STANDARD_COMPRESSION_UNIT : 0; - /* If @lowest_vcn == 0, than setup empty attribute. */ - if (!lowest_vcn) { - a->highest_vcn = cpu_to_sle64(-1); - a->allocated_size = 0; - a->data_size = 0; - a->initialized_size = 0; - /* Set empty mapping pairs. */ - *((u8*)a + le16_to_cpu(a->mapping_pairs_offset)) = 0; - } - if (name_len) - memcpy((u8*)a + le16_to_cpu(a->name_offset), - name, sizeof(ntfschar) * name_len); - m->next_attr_instance = - cpu_to_le16((le16_to_cpu(m->next_attr_instance) + 1) & 0xffff); - if (ni->nr_extents == -1) - base_ni = ni->base_ni; - else - base_ni = ni; - if (type != AT_ATTRIBUTE_LIST && NInoAttrList(base_ni)) { - if (ntfs_attrlist_entry_add(ni, a)) { - err = errno; - ntfs_log_perror("Failed add attr entry to attrlist"); - ntfs_attr_record_resize(m, a, 0); - goto put_err_out; - } - } - ntfs_inode_mark_dirty(ni); - /* - * Locate offset from start of the MFT record where new attribute is - * placed. We need relookup it, because record maybe moved during - * update of attribute list. - */ - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, - lowest_vcn, NULL, 0, ctx)) { - ntfs_log_perror("%s: attribute lookup failed", __FUNCTION__); - ntfs_attr_put_search_ctx(ctx); - return -1; - - } - offset = (u8*)ctx->attr - (u8*)ctx->mrec; - ntfs_attr_put_search_ctx(ctx); - return offset; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_record_rm - remove attribute extent - * @ctx: search context describing the attribute which should be removed - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error. On error the error code is stored in - * errno. Possible error codes are: - * EINVAL - Invalid arguments passed to function. - * EIO - I/O error occurred or damaged filesystem. - */ -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; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) ctx->ntfs_ino->mft_no, - (unsigned) le32_to_cpu(ctx->attr->type)); - type = ctx->attr->type; - ni = ctx->ntfs_ino; - if (ctx->base_ntfs_ino) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - - /* Remove attribute itself. */ - if (ntfs_attr_record_resize(ctx->mrec, ctx->attr, 0)) { - ntfs_log_trace("Couldn't remove attribute record. Bug or damaged MFT " - "record.\n"); - if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) - if (ntfs_attrlist_entry_add(ni, ctx->attr)) - ntfs_log_trace("Rollback failed. Leaving inconstant " - "metadata.\n"); - err = EIO; - return -1; - } - ntfs_inode_mark_dirty(ni); - - /* - * Remove record from $ATTRIBUTE_LIST if present and we don't want - * delete $ATTRIBUTE_LIST itself. - */ - if (NInoAttrList(base_ni) && type != AT_ATTRIBUTE_LIST) { - if (ntfs_attrlist_entry_rm(ctx)) { - ntfs_log_trace("Couldn't delete record from " - "$ATTRIBUTE_LIST.\n"); - return -1; - } - } - - /* Post $ATTRIBUTE_LIST delete setup. */ - if (type == AT_ATTRIBUTE_LIST) { - if (NInoAttrList(base_ni) && base_ni->attr_list) - free(base_ni->attr_list); - base_ni->attr_list = NULL; - NInoClearAttrList(base_ni); - NInoAttrListClearDirty(base_ni); - } - - /* Free MFT record, if it doesn't contain attributes. */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) - - le16_to_cpu(ctx->mrec->attrs_offset) == 8) { - if (ntfs_mft_record_free(ni->vol, ni)) { - // FIXME: We need rollback here. - ntfs_log_trace("Couldn't free MFT record.\n"); - errno = EIO; - return -1; - } - /* Remove done if we freed base inode. */ - if (ni == base_ni) - return 0; - } - - if (type == AT_ATTRIBUTE_LIST || !NInoAttrList(base_ni)) - return 0; - - /* Remove attribute list if we don't need it any more. */ - if (!ntfs_attrlist_need(base_ni)) { - ntfs_attr_reinit_search_ctx(ctx); - if (ntfs_attr_lookup(AT_ATTRIBUTE_LIST, NULL, 0, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - /* - * FIXME: Should we succeed here? Definitely something - * goes wrong because NInoAttrList(base_ni) returned - * that we have got attribute list. - */ - ntfs_log_trace("Couldn't find attribute list. Succeed " - "anyway.\n"); - return 0; - } - /* Deallocate clusters. */ - if (ctx->attr->non_resident) { - runlist *al_rl; - - al_rl = ntfs_mapping_pairs_decompress(base_ni->vol, - ctx->attr, NULL); - if (!al_rl) { - ntfs_log_trace("Couldn't decompress attribute list " - "runlist. Succeed anyway.\n"); - return 0; - } - if (ntfs_cluster_free_from_rl(base_ni->vol, al_rl)) { - ntfs_log_trace("Leaking clusters! Run chkdsk. " - "Couldn't free clusters from " - "attribute list runlist.\n"); - } - free(al_rl); - } - /* Remove attribute record itself. */ - if (ntfs_attr_record_rm(ctx)) { - /* - * FIXME: Should we succeed here? BTW, chkdsk doesn't - * complain if it find MFT record with attribute list, - * but without extents. - */ - ntfs_log_trace("Couldn't remove attribute list. Succeed " - "anyway.\n"); - return 0; - } - } - return 0; -} - -/** - * ntfs_attr_add - add attribute to inode - * @ni: opened ntfs inode to which add attribute - * @type: type of the new attribute - * @name: name in unicode of the new attribute - * @name_len: name length in unicode characters of the new attribute - * @val: value of new attribute - * @size: size of the new attribute / length of @val (if specified) - * - * @val should always be specified for always resident attributes (eg. FILE_NAME - * attribute), for attributes that can become non-resident @val can be NULL - * (eg. DATA attribute). @size can be specified even if @val is NULL, in this - * case data size will be equal to @size and initialized size will be equal - * to 0. - * - * If inode haven't got enough space to add attribute, add attribute to one of - * it extents, if no extents present or no one of them have enough space, than - * allocate new extent and add attribute to it. - * - * If on one of this steps attribute list is needed but not present, than it is - * added transparently to caller. So, this function should not be called with - * @type == AT_ATTRIBUTE_LIST, if you really need to add attribute list call - * ntfs_inode_add_attrlist instead. - * - * On success return 0. On error return -1 with errno set to the error code. - */ -int ntfs_attr_add(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, u8 *val, s64 size) -{ - u32 attr_rec_size; - int err, i, offset; - BOOL is_resident; - BOOL can_be_non_resident = FALSE; - ntfs_inode *attr_ni; - ntfs_attr *na; - ATTR_FLAGS data_flags; - - if (!ni || size < 0 || type == AT_ATTRIBUTE_LIST) { - errno = EINVAL; - ntfs_log_perror("%s: ni=%p size=%lld", __FUNCTION__, ni, - (long long)size); - return -1; - } - - ntfs_log_trace("Entering for inode %lld, attr %x, size %lld.\n", - (long long)ni->mft_no, type, (long long)size); - - if (ni->nr_extents == -1) - ni = ni->base_ni; - - /* Check the attribute type and the size. */ - if (ntfs_attr_size_bounds_check(ni->vol, type, size)) { - if (errno == ENOENT) - errno = EIO; - return -1; - } - - /* Sanity checks for always resident attributes. */ - if (ntfs_attr_can_be_non_resident(ni->vol, type)) { - if (errno != EPERM) { - err = errno; - ntfs_log_perror("ntfs_attr_can_be_non_resident failed"); - goto err_out; - } - /* @val is mandatory. */ - if (!val) { - errno = EINVAL; - ntfs_log_perror("val is mandatory for always resident " - "attributes"); - return -1; - } - if (size > ni->vol->mft_record_size) { - errno = ERANGE; - ntfs_log_perror("Attribute is too big"); - return -1; - } - } else - can_be_non_resident = TRUE; - - /* - * Determine resident or not will be new attribute. We add 8 to size in - * non resident case for mapping pairs. - */ - if (!ntfs_attr_can_be_resident(ni->vol, type)) { - is_resident = TRUE; - } else { - if (errno != EPERM) { - err = errno; - ntfs_log_perror("ntfs_attr_can_be_resident failed"); - goto err_out; - } - is_resident = FALSE; - } - /* Calculate attribute record size. */ - if (is_resident) - attr_rec_size = offsetof(ATTR_RECORD, resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + - ((size + 7) & ~7); - else - attr_rec_size = offsetof(ATTR_RECORD, non_resident_end) + - ((name_len * sizeof(ntfschar) + 7) & ~7) + 8; - - /* - * If we have enough free space for the new attribute in the base MFT - * record, then add attribute to it. - */ - if (le32_to_cpu(ni->mrec->bytes_allocated) - - le32_to_cpu(ni->mrec->bytes_in_use) >= attr_rec_size) { - attr_ni = ni; - goto add_attr_record; - } - - /* Try to add to extent inodes. */ - if (ntfs_inode_attach_all_extents(ni)) { - err = errno; - ntfs_log_perror("Failed to attach all extents to inode"); - goto err_out; - } - for (i = 0; i < ni->nr_extents; i++) { - attr_ni = ni->extent_nis[i]; - if (le32_to_cpu(attr_ni->mrec->bytes_allocated) - - le32_to_cpu(attr_ni->mrec->bytes_in_use) >= - attr_rec_size) - goto add_attr_record; - } - - /* There is no extent that contain enough space for new attribute. */ - if (!NInoAttrList(ni)) { - /* Add attribute list not present, add it and retry. */ - if (ntfs_inode_add_attrlist(ni)) { - err = errno; - ntfs_log_perror("Failed to add attribute list"); - goto err_out; - } - return ntfs_attr_add(ni, type, name, name_len, val, size); - } - /* Allocate new extent. */ - attr_ni = ntfs_mft_record_alloc(ni->vol, ni); - if (!attr_ni) { - err = errno; - ntfs_log_perror("Failed to allocate extent record"); - goto err_out; - } - -add_attr_record: - if ((ni->flags & FILE_ATTR_COMPRESSED) - && ((type == AT_DATA) - || ((type == AT_INDEX_ROOT) && (name == NTFS_INDEX_I30)))) - data_flags = ATTR_IS_COMPRESSED; - else - data_flags = const_cpu_to_le16(0); - if (is_resident) { - /* Add resident attribute. */ - offset = ntfs_resident_attr_record_add(attr_ni, type, name, - name_len, val, size, data_flags); - if (offset < 0) { - if (errno == ENOSPC && can_be_non_resident) - goto add_non_resident; - err = errno; - ntfs_log_perror("Failed to add resident attribute"); - goto free_err_out; - } - return 0; - } - -add_non_resident: - /* Add non resident attribute. */ - offset = ntfs_non_resident_attr_record_add(attr_ni, type, name, - name_len, 0, 8, data_flags); - if (offset < 0) { - err = errno; - ntfs_log_perror("Failed to add non resident attribute"); - goto free_err_out; - } - - /* If @size == 0, we are done. */ - if (!size) - return 0; - - /* Open new attribute and resize it. */ - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - err = errno; - ntfs_log_perror("Failed to open just added attribute"); - goto rm_attr_err_out; - } - /* Resize and set attribute value. */ - if (ntfs_attr_truncate(na, size) || - (val && (ntfs_attr_pwrite(na, 0, size, val) != size))) { - err = errno; - ntfs_log_perror("Failed to initialize just added attribute"); - if (ntfs_attr_rm(na)) - ntfs_log_perror("Failed to remove just added attribute"); - ntfs_attr_close(na); - goto err_out; - } - ntfs_attr_close(na); - return 0; - -rm_attr_err_out: - /* Remove just added attribute. */ - if (ntfs_attr_record_resize(attr_ni->mrec, - (ATTR_RECORD*)((u8*)attr_ni->mrec + offset), 0)) - ntfs_log_perror("Failed to remove just added attribute #2"); -free_err_out: - /* Free MFT record, if it doesn't contain attributes. */ - if (le32_to_cpu(attr_ni->mrec->bytes_in_use) - - le16_to_cpu(attr_ni->mrec->attrs_offset) == 8) - if (ntfs_mft_record_free(attr_ni->vol, attr_ni)) - ntfs_log_perror("Failed to free MFT record"); -err_out: - errno = err; - return -1; -} - -/* - * Change an attribute flag - */ - -int ntfs_attr_set_flags(ntfs_inode *ni, ATTR_TYPES type, - ntfschar *name, u8 name_len, ATTR_FLAGS flags, ATTR_FLAGS mask) -{ - ntfs_attr_search_ctx *ctx; - int res; - - res = -1; - /* Search for designated attribute */ - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (ctx) { - if (!ntfs_attr_lookup(type, name, name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - /* do the requested change (all small endian le16) */ - ctx->attr->flags = (ctx->attr->flags & ~mask) - | (flags & mask); - NInoSetDirty(ni); - res = 0; - } - ntfs_attr_put_search_ctx(ctx); - } - return (res); -} - - -/** - * ntfs_attr_rm - remove attribute from ntfs inode - * @na: opened ntfs attribute to delete - * - * Remove attribute and all it's extents from ntfs inode. If attribute was non - * resident also free all clusters allocated by attribute. - * - * Return 0 on success or -1 on error with errno set to the error code. - */ -int ntfs_attr_rm(ntfs_attr *na) -{ - ntfs_attr_search_ctx *ctx; - int ret = 0; - - if (!na) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", - (long long) na->ni->mft_no, na->type); - - /* Free cluster allocation. */ - if (NAttrNonResident(na)) { - if (ntfs_attr_map_whole_runlist(na)) - return -1; - if (ntfs_cluster_free(na->ni->vol, na, 0, -1) < 0) { - ntfs_log_trace("Failed to free cluster allocation. Leaving " - "inconstant metadata.\n"); - ret = -1; - } - } - - /* Search for attribute extents and remove them all. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (ntfs_attr_record_rm(ctx)) { - ntfs_log_trace("Failed to remove attribute extent. Leaving " - "inconstant metadata.\n"); - ret = -1; - } - ntfs_attr_reinit_search_ctx(ctx); - } - ntfs_attr_put_search_ctx(ctx); - if (errno != ENOENT) { - ntfs_log_trace("Attribute lookup failed. Probably leaving inconstant " - "metadata.\n"); - ret = -1; - } - - return ret; -} - -/** - * ntfs_attr_record_resize - resize an attribute record - * @m: mft record containing attribute record - * @a: attribute record to resize - * @new_size: new size in bytes to which to resize the attribute record @a - * - * Resize the attribute record @a, i.e. the resident part of the attribute, in - * the mft record @m to @new_size bytes. - * - * Return 0 on success and -1 on error with errno set to the error code. - * The following error codes are defined: - * ENOSPC - Not enough space in the mft record @m to perform the resize. - * Note that on error no modifications have been performed whatsoever. - * - * Warning: If you make a record smaller without having copied all the data you - * are interested in the data may be overwritten! - */ -int ntfs_attr_record_resize(MFT_RECORD *m, ATTR_RECORD *a, u32 new_size) -{ - u32 old_size, alloc_size, attr_size; - - old_size = le32_to_cpu(m->bytes_in_use); - alloc_size = le32_to_cpu(m->bytes_allocated); - attr_size = le32_to_cpu(a->length); - - ntfs_log_trace("Sizes: old=%u alloc=%u attr=%u new=%u\n", - (unsigned)old_size, (unsigned)alloc_size, - (unsigned)attr_size, (unsigned)new_size); - - /* Align to 8 bytes, just in case the caller hasn't. */ - new_size = (new_size + 7) & ~7; - - /* If the actual attribute length has changed, move things around. */ - if (new_size != attr_size) { - - u32 new_muse = old_size - attr_size + new_size; - - /* Not enough space in this mft record. */ - if (new_muse > alloc_size) { - errno = ENOSPC; - ntfs_log_trace("Not enough space in the MFT record " - "(%u > %u)\n", new_muse, alloc_size); - return -1; - } - - if (a->type == AT_INDEX_ROOT && new_size > attr_size && - new_muse + 120 > alloc_size && old_size + 120 <= alloc_size) { - errno = ENOSPC; - ntfs_log_trace("Too big INDEX_ROOT (%u > %u)\n", - new_muse, alloc_size); - return STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; - } - - /* Move attributes following @a to their new location. */ - memmove((u8 *)a + new_size, (u8 *)a + attr_size, - old_size - ((u8 *)a - (u8 *)m) - attr_size); - - /* Adjust @m to reflect the change in used space. */ - m->bytes_in_use = cpu_to_le32(new_muse); - - /* Adjust @a to reflect the new size. */ - if (new_size >= offsetof(ATTR_REC, length) + sizeof(a->length)) - a->length = cpu_to_le32(new_size); - } - return 0; -} - -/** - * ntfs_resident_attr_value_resize - resize the value of a resident attribute - * @m: mft record containing attribute record - * @a: attribute record whose value to resize - * @new_size: new size in bytes to which to resize the attribute value of @a - * - * Resize the value of the attribute @a in the mft record @m to @new_size bytes. - * If the value is made bigger, the newly "allocated" space is cleared. - * - * Return 0 on success and -1 on error with errno set to the error code. - * The following error codes are defined: - * ENOSPC - Not enough space in the mft record @m to perform the resize. - * Note that on error no modifications have been performed whatsoever. - */ -int ntfs_resident_attr_value_resize(MFT_RECORD *m, ATTR_RECORD *a, - const u32 new_size) -{ - int ret; - - ntfs_log_trace("Entering for new size %u.\n", (unsigned)new_size); - - /* Resize the resident part of the attribute record. */ - if ((ret = ntfs_attr_record_resize(m, a, (le16_to_cpu(a->value_offset) + - new_size + 7) & ~7)) < 0) - return ret; - /* - * If we made the attribute value bigger, clear the area between the - * old size and @new_size. - */ - if (new_size > le32_to_cpu(a->value_length)) - memset((u8*)a + le16_to_cpu(a->value_offset) + - le32_to_cpu(a->value_length), 0, new_size - - le32_to_cpu(a->value_length)); - /* Finally update the length of the attribute value. */ - a->value_length = cpu_to_le32(new_size); - return 0; -} - -/** - * ntfs_attr_record_move_to - move attribute record to target inode - * @ctx: attribute search context describing the attribute record - * @ni: opened ntfs inode to which move attribute record - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_record_move_to(ntfs_attr_search_ctx *ctx, ntfs_inode *ni) -{ - ntfs_attr_search_ctx *nctx; - ATTR_RECORD *a; - int err; - - if (!ctx || !ctx->attr || !ctx->ntfs_ino || !ni) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return -1; - } - - ntfs_log_trace("Entering for ctx->attr->type 0x%x, ctx->ntfs_ino->mft_no " - "0x%llx, ni->mft_no 0x%llx.\n", - (unsigned) le32_to_cpu(ctx->attr->type), - (long long) ctx->ntfs_ino->mft_no, - (long long) ni->mft_no); - - if (ctx->ntfs_ino == ni) - return 0; - - if (!ctx->al_entry) { - ntfs_log_trace("Inode should contain attribute list to use this " - "function.\n"); - errno = EINVAL; - return -1; - } - - /* Find place in MFT record where attribute will be moved. */ - a = ctx->attr; - nctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!nctx) - return -1; - - /* - * Use ntfs_attr_find instead of ntfs_attr_lookup to find place for - * attribute in @ni->mrec, not any extent inode in case if @ni is base - * file record. - */ - if (!ntfs_attr_find(a->type, (ntfschar*)((u8*)a + le16_to_cpu( - a->name_offset)), a->name_length, CASE_SENSITIVE, NULL, - 0, nctx)) { - ntfs_log_trace("Attribute of such type, with same name already " - "present in this MFT record.\n"); - err = EEXIST; - goto put_err_out; - } - if (errno != ENOENT) { - err = errno; - ntfs_log_debug("Attribute lookup failed.\n"); - goto put_err_out; - } - - /* Make space and move attribute. */ - if (ntfs_make_room_for_attr(ni->mrec, (u8*) nctx->attr, - le32_to_cpu(a->length))) { - err = errno; - ntfs_log_trace("Couldn't make space for attribute.\n"); - goto put_err_out; - } - memcpy(nctx->attr, a, le32_to_cpu(a->length)); - nctx->attr->instance = nctx->mrec->next_attr_instance; - nctx->mrec->next_attr_instance = cpu_to_le16( - (le16_to_cpu(nctx->mrec->next_attr_instance) + 1) & 0xffff); - ntfs_attr_record_resize(ctx->mrec, a, 0); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_inode_mark_dirty(ni); - - /* Update attribute list. */ - ctx->al_entry->mft_reference = - MK_LE_MREF(ni->mft_no, le16_to_cpu(ni->mrec->sequence_number)); - ctx->al_entry->instance = nctx->attr->instance; - ntfs_attrlist_mark_dirty(ni); - - ntfs_attr_put_search_ctx(nctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(nctx); - errno = err; - return -1; -} - -/** - * ntfs_attr_record_move_away - move away attribute record from it's mft record - * @ctx: attribute search context describing the attribute record - * @extra: minimum amount of free space in the new holder of record - * - * New attribute record holder must have free @extra bytes after moving - * attribute record to it. - * - * If this function succeed, user should reinit search context if he/she wants - * use it anymore. - * - * Return 0 on success and -1 on error with errno set to the error code. - */ -int ntfs_attr_record_move_away(ntfs_attr_search_ctx *ctx, int extra) -{ - ntfs_inode *base_ni, *ni; - MFT_RECORD *m; - int i; - - if (!ctx || !ctx->attr || !ctx->ntfs_ino || extra < 0) { - errno = EINVAL; - ntfs_log_perror("%s: ctx=%p ctx->attr=%p extra=%d", __FUNCTION__, - ctx, ctx ? ctx->attr : NULL, extra); - return -1; - } - - ntfs_log_trace("Entering for attr 0x%x, inode %llu\n", - (unsigned) le32_to_cpu(ctx->attr->type), - (unsigned long long)ctx->ntfs_ino->mft_no); - - if (ctx->ntfs_ino->nr_extents == -1) - base_ni = ctx->base_ntfs_ino; - else - base_ni = ctx->ntfs_ino; - - if (!NInoAttrList(base_ni)) { - errno = EINVAL; - ntfs_log_perror("Inode %llu has no attrlist", - (unsigned long long)base_ni->mft_no); - return -1; - } - - if (ntfs_inode_attach_all_extents(ctx->ntfs_ino)) { - ntfs_log_perror("Couldn't attach extents, inode=%llu", - (unsigned long long)base_ni->mft_no); - return -1; - } - - /* Walk through all extents and try to move attribute to them. */ - for (i = 0; i < base_ni->nr_extents; i++) { - ni = base_ni->extent_nis[i]; - m = ni->mrec; - - if (ctx->ntfs_ino->mft_no == ni->mft_no) - continue; - - if (le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) < - le32_to_cpu(ctx->attr->length) + extra) - continue; - - /* - * ntfs_attr_record_move_to can fail if extent with other lowest - * VCN already present in inode we trying move record to. So, - * do not return error. - */ - if (!ntfs_attr_record_move_to(ctx, ni)) - return 0; - } - - /* - * Failed to move attribute to one of the current extents, so allocate - * new extent and move attribute to it. - */ - ni = ntfs_mft_record_alloc(base_ni->vol, base_ni); - if (!ni) { - ntfs_log_perror("Couldn't allocate MFT record"); - return -1; - } - if (ntfs_attr_record_move_to(ctx, ni)) { - ntfs_log_perror("Couldn't move attribute to MFT record"); - return -1; - } - return 0; -} - -/** - * ntfs_attr_make_non_resident - convert a resident to a non-resident attribute - * @na: open ntfs attribute to make non-resident - * @ctx: ntfs search context describing the attribute - * - * Convert a resident ntfs attribute to a non-resident one. - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EPERM - The attribute is not allowed to be non-resident. - * TODO: others... - * - * NOTE to self: No changes in the attribute list are required to move from - * a resident to a non-resident attribute. - * - * Warning: We do not set the inode dirty and we do not write out anything! - * We expect the caller to do this as this is a fairly low level - * function and it is likely there will be further changes made. - */ -int ntfs_attr_make_non_resident(ntfs_attr *na, - ntfs_attr_search_ctx *ctx) -{ - s64 new_allocated_size, bw; - ntfs_volume *vol = na->ni->vol; - ATTR_REC *a = ctx->attr; - runlist *rl; - int mp_size, mp_ofs, name_ofs, arec_size, err; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - /* Some preliminary sanity checking. */ - if (NAttrNonResident(na)) { - ntfs_log_trace("Eeek! Trying to make non-resident attribute " - "non-resident. Aborting...\n"); - errno = EINVAL; - return -1; - } - - /* Check that the attribute is allowed to be non-resident. */ - if (ntfs_attr_can_be_non_resident(vol, na->type)) - return -1; - - new_allocated_size = (le32_to_cpu(a->value_length) + vol->cluster_size - - 1) & ~(vol->cluster_size - 1); - - if (new_allocated_size > 0) { - /* 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); - if (!rl) - return -1; - } else - rl = NULL; - /* - * Setup the in-memory attribute structure to be non-resident so that - * we can use ntfs_attr_pwrite(). - */ - NAttrSetNonResident(na); - na->rl = rl; - na->allocated_size = new_allocated_size; - na->data_size = na->initialized_size = le32_to_cpu(a->value_length); - /* - * FIXME: For now just clear all of these as we don't support them when - * writing. - */ - NAttrClearSparse(na); - NAttrClearEncrypted(na); - if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { - /* set compression writing parameters */ - na->compression_block_size - = 1 << (STANDARD_COMPRESSION_UNIT + vol->cluster_size_bits); - na->compression_block_clusters = 1 << STANDARD_COMPRESSION_UNIT; - } - - if (rl) { - /* Now copy the attribute value to the allocated cluster(s). */ - bw = ntfs_attr_pwrite(na, 0, le32_to_cpu(a->value_length), - (u8*)a + le16_to_cpu(a->value_offset)); - if (bw != le32_to_cpu(a->value_length)) { - err = errno; - ntfs_log_debug("Eeek! Failed to write out attribute value " - "(bw = %lli, errno = %i). " - "Aborting...\n", (long long)bw, err); - if (bw >= 0) - err = EIO; - goto cluster_free_err_out; - } - } - /* Determine the size of the mapping pairs array. */ - mp_size = ntfs_get_size_for_mapping_pairs(vol, rl, 0, INT_MAX); - if (mp_size < 0) { - err = errno; - ntfs_log_debug("Eeek! Failed to get size for mapping pairs array. " - "Aborting...\n"); - goto cluster_free_err_out; - } - /* Calculate new offsets for the name and the mapping pairs array. */ - if (na->ni->flags & FILE_ATTR_COMPRESSED) - name_ofs = (sizeof(ATTR_REC) + 7) & ~7; - else - name_ofs = (sizeof(ATTR_REC) - sizeof(a->compressed_size) + 7) & ~7; - mp_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; - /* - * Determine the size of the resident part of the non-resident - * attribute record. (Not compressed thus no compressed_size element - * present.) - */ - arec_size = (mp_ofs + mp_size + 7) & ~7; - - /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { - err = errno; - goto cluster_free_err_out; - } - - /* - * Convert the resident part of the attribute record to describe a - * non-resident attribute. - */ - a->non_resident = 1; - - /* Move the attribute name if it exists and update the offset. */ - if (a->name_length) - memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - a->name_offset = cpu_to_le16(name_ofs); - - /* Setup the fields specific to non-resident attributes. */ - a->lowest_vcn = cpu_to_sle64(0); - a->highest_vcn = cpu_to_sle64((new_allocated_size - 1) >> - vol->cluster_size_bits); - - a->mapping_pairs_offset = cpu_to_le16(mp_ofs); - - /* - * Update the flags to match the in-memory ones. - * However cannot change the compression state if we had - * a fuse_file_info open with a mark for release. - * The decisions about compression can only be made when - * creating/recreating the stream, not when making non resident. - */ - a->flags &= ~(ATTR_IS_SPARSE | ATTR_IS_ENCRYPTED); - if ((a->flags & ATTR_COMPRESSION_MASK) == ATTR_IS_COMPRESSED) { - /* support only ATTR_IS_COMPRESSED compression mode */ - a->compression_unit = STANDARD_COMPRESSION_UNIT; - a->compressed_size = const_cpu_to_le64(0); - } else { - a->compression_unit = 0; - a->flags &= ~ATTR_COMPRESSION_MASK; - na->data_flags = a->flags; - } - - memset(&a->reserved1, 0, sizeof(a->reserved1)); - - a->allocated_size = cpu_to_sle64(new_allocated_size); - a->data_size = a->initialized_size = cpu_to_sle64(na->data_size); - - /* Generate the mapping pairs array in the attribute record. */ - if (ntfs_mapping_pairs_build(vol, (u8*)a + mp_ofs, arec_size - mp_ofs, - rl, 0, NULL) < 0) { - // FIXME: Eeek! We need rollback! (AIA) - ntfs_log_trace("Eeek! Failed to build mapping pairs. Leaving " - "corrupt attribute record on disk. In memory " - "runlist is still intact! Error code is %i. " - "FIXME: Need to rollback instead!\n", errno); - return -1; - } - - /* Done! */ - return 0; - -cluster_free_err_out: - if (rl && ntfs_cluster_free(vol, na, 0, -1) < 0) - ntfs_log_trace("Eeek! Failed to release allocated clusters in error " - "code path. Leaving inconsistent metadata...\n"); - NAttrClearNonResident(na); - na->allocated_size = na->data_size; - na->rl = NULL; - free(rl); - errno = err; - return -1; -} - - -static int ntfs_resident_attr_resize(ntfs_attr *na, const s64 newsize); - -/** - * ntfs_resident_attr_resize - resize a resident, open ntfs attribute - * @na: resident ntfs attribute to resize - * @newsize: new size (in bytes) to which to resize the attribute - * - * Change the size of a resident, open ntfs attribute @na to @newsize bytes. - * - * On success return 0 - * On error return values are: - * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT - * STATUS_ERROR - otherwise - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * 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) -{ - ntfs_attr_search_ctx *ctx; - ntfs_volume *vol; - ntfs_inode *ni; - int err, ret = STATUS_ERROR; - - ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize); - - /* Get the attribute record that needs modification. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - if (ntfs_attr_lookup(na->type, na->name, na->name_len, 0, 0, NULL, 0, - ctx)) { - err = errno; - ntfs_log_perror("ntfs_attr_lookup failed"); - goto put_err_out; - } - vol = na->ni->vol; - /* - * Check the attribute type and the corresponding minimum and maximum - * sizes against @newsize and fail if @newsize is out of bounds. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - err = errno; - if (err == ENOENT) - err = EIO; - ntfs_log_perror("%s: bounds check failed", __FUNCTION__); - goto put_err_out; - } - /* - * If @newsize is bigger than the mft record we need to make the - * 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) { - /* Perform the resize of the attribute record. */ - if (!(ret = ntfs_resident_attr_value_resize(ctx->mrec, ctx->attr, - newsize))) { - /* Update attribute size everywhere. */ - na->data_size = na->initialized_size = newsize; - na->allocated_size = (newsize + 7) & ~7; - if ((na->data_flags & ATTR_COMPRESSION_MASK) - || NAttrSparse(na)) - na->compressed_size = na->allocated_size; - if (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); - } - goto resize_done; - } - /* Prefer AT_INDEX_ALLOCATION instead of AT_ATTRIBUTE_LIST */ - if (ret == STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT) { - err = errno; - goto put_err_out; - } - } - /* There is not enough space in the mft record to perform the resize. */ - - /* Make the attribute non-resident if possible. */ - if (!ntfs_attr_make_non_resident(na, ctx)) { - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - /* Resize non-resident attribute */ - return ntfs_attr_truncate(na, newsize); - } else if (errno != ENOSPC && errno != EPERM) { - err = errno; - ntfs_log_perror("Failed to make attribute non-resident"); - goto put_err_out; - } - - /* Try to make other attributes non-resident and retry each time. */ - ntfs_attr_init_search_ctx(ctx, NULL, na->ni->mrec); - while (!ntfs_attr_lookup(AT_UNUSED, NULL, 0, 0, 0, NULL, 0, ctx)) { - ntfs_attr *tna; - ATTR_RECORD *a; - - a = ctx->attr; - if (a->non_resident) - continue; - - /* - * Check out whether convert is reasonable. Assume that mapping - * pairs will take 8 bytes. - */ - if (le32_to_cpu(a->length) <= offsetof(ATTR_RECORD, - compressed_size) + ((a->name_length * - sizeof(ntfschar) + 7) & ~7) + 8) - continue; - - tna = ntfs_attr_open(na->ni, a->type, (ntfschar*)((u8*)a + - le16_to_cpu(a->name_offset)), a->name_length); - if (!tna) { - err = errno; - ntfs_log_perror("Couldn't open attribute"); - goto put_err_out; - } - if (ntfs_attr_make_non_resident(tna, ctx)) { - ntfs_attr_close(tna); - continue; - } - ntfs_inode_mark_dirty(tna->ni); - ntfs_attr_close(tna); - ntfs_attr_put_search_ctx(ctx); - return ntfs_resident_attr_resize(na, newsize); - } - /* Check whether error occurred. */ - if (errno != ENOENT) { - err = errno; - ntfs_log_perror("%s: Attribute lookup failed 1", __FUNCTION__); - goto put_err_out; - } - - /* - * The standard information and attribute list attributes can't be - * moved out from the base MFT record, so try to move out others. - */ - if (na->type==AT_STANDARD_INFORMATION || na->type==AT_ATTRIBUTE_LIST) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_free_space(na->ni, offsetof(ATTR_RECORD, - non_resident_end) + 8)) { - ntfs_log_perror("Could not free space in MFT record"); - return -1; - } - return ntfs_resident_attr_resize(na, newsize); - } - - /* - * Move the attribute to a new mft record, creating an attribute list - * attribute or modifying it if it is already present. - */ - - /* Point search context back to attribute which we need resize. */ - ntfs_attr_init_search_ctx(ctx, na->ni, NULL); - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - ntfs_log_perror("%s: Attribute lookup failed 2", __FUNCTION__); - err = errno; - goto put_err_out; - } - - /* - * Check whether attribute is already single in this MFT record. - * 8 added for the attribute terminator. - */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) == - le16_to_cpu(ctx->mrec->attrs_offset) + - le32_to_cpu(ctx->attr->length) + 8) { - err = ENOSPC; - ntfs_log_trace("MFT record is filled with one attribute\n"); - ret = STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT; - goto put_err_out; - } - - /* Add attribute list if not present. */ - if (na->ni->nr_extents == -1) - ni = na->ni->base_ni; - else - ni = na->ni; - if (!NInoAttrList(ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(ni)) - return -1; - return ntfs_resident_attr_resize(na, newsize); - } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(vol, ni); - if (!ni) { - err = errno; - ntfs_log_perror("Couldn't allocate new MFT record"); - goto put_err_out; - } - /* Move attribute to it. */ - if (ntfs_attr_record_move_to(ctx, ni)) { - err = errno; - ntfs_log_perror("Couldn't move attribute to new MFT record"); - goto put_err_out; - } - /* Update ntfs attribute. */ - if (na->ni->nr_extents == -1) - na->ni = ni; - - ntfs_attr_put_search_ctx(ctx); - /* Try to perform resize once again. */ - return ntfs_resident_attr_resize(na, newsize); - -resize_done: - /* - * Set the inode (and its base inode if it exists) dirty so it is - * written out later. - */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - ntfs_attr_put_search_ctx(ctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return ret; -} - -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); - ntfs_log_leave("\n"); - return ret; -} - -/** - * ntfs_attr_make_resident - convert a non-resident to a resident attribute - * @na: open ntfs attribute to make resident - * @ctx: ntfs search context describing the attribute - * - * Convert a non-resident ntfs attribute to a resident one. - * - * Return 0 on success and -1 on error with errno set to the error code. The - * following error codes are defined: - * EINVAL - Invalid arguments passed. - * EPERM - The attribute is not allowed to be resident. - * EIO - I/O error, damaged inode or bug. - * ENOSPC - There is no enough space to perform conversion. - * EOPNOTSUPP - Requested conversion is not supported yet. - * - * Warning: We do not set the inode dirty and we do not write out anything! - * We expect the caller to do this as this is a fairly low level - * function and it is likely there will be further changes made. - */ -static int ntfs_attr_make_resident(ntfs_attr *na, ntfs_attr_search_ctx *ctx) -{ - ntfs_volume *vol = na->ni->vol; - ATTR_REC *a = ctx->attr; - int name_ofs, val_ofs, err = EIO; - s64 arec_size, bytes_read; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x.\n", (unsigned long - long)na->ni->mft_no, na->type); - - /* Should be called for the first extent of the attribute. */ - 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; - return -1; - } - - /* Some preliminary sanity checking. */ - if (!NAttrNonResident(na)) { - ntfs_log_trace("Eeek! Trying to make resident attribute resident. " - "Aborting...\n"); - errno = EINVAL; - return -1; - } - - /* Make sure this is not $MFT/$BITMAP or Windows will not boot! */ - if (na->type == AT_BITMAP && na->ni->mft_no == FILE_MFT) { - errno = EPERM; - return -1; - } - - /* Check that the attribute is allowed to be resident. */ - if (ntfs_attr_can_be_resident(vol, na->type)) - return -1; - - if (na->data_flags & ATTR_IS_ENCRYPTED) { - ntfs_log_trace("Making encrypted streams resident is not " - "implemented yet.\n"); - errno = EOPNOTSUPP; - return -1; - } - - /* Work out offsets into and size of the resident attribute. */ - name_ofs = 24; /* = sizeof(resident_ATTR_REC); */ - val_ofs = (name_ofs + a->name_length * sizeof(ntfschar) + 7) & ~7; - arec_size = (val_ofs + na->data_size + 7) & ~7; - - /* Sanity check the size before we start modifying the attribute. */ - if (le32_to_cpu(ctx->mrec->bytes_in_use) - le32_to_cpu(a->length) + - arec_size > le32_to_cpu(ctx->mrec->bytes_allocated)) { - errno = ENOSPC; - ntfs_log_trace("Not enough space to make attribute resident\n"); - return -1; - } - - /* Read and cache the whole runlist if not already done. */ - if (ntfs_attr_map_whole_runlist(na)) - return -1; - - /* Move the attribute name if it exists and update the offset. */ - if (a->name_length) { - memmove((u8*)a + name_ofs, (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - } - a->name_offset = cpu_to_le16(name_ofs); - - /* Resize the resident part of the attribute record. */ - if (ntfs_attr_record_resize(ctx->mrec, a, arec_size) < 0) { - /* - * Bug, because ntfs_attr_record_resize should not fail (we - * already checked that attribute fits MFT record). - */ - ntfs_log_error("BUG! Failed to resize attribute record. " - "Please report to the %s. Aborting...\n", - NTFS_DEV_LIST); - errno = EIO; - return -1; - } - - /* Convert the attribute record to describe a resident attribute. */ - a->non_resident = 0; - a->flags = 0; - a->value_length = cpu_to_le32(na->data_size); - a->value_offset = cpu_to_le16(val_ofs); - /* - * If a data stream was wiped out, adjust the compression mode - * to current state of compression flag - */ - if (!na->data_size - && (na->type == AT_DATA) - && (na->ni->flags & FILE_ATTR_COMPRESSED)) { - a->flags |= ATTR_IS_COMPRESSED; - na->data_flags = a->flags; - } - /* - * File names cannot be non-resident so we would never see this here - * but at least it serves as a reminder that there may be attributes - * for which we do need to set this flag. (AIA) - */ - if (a->type == AT_FILE_NAME) - a->resident_flags = RESIDENT_ATTR_IS_INDEXED; - else - a->resident_flags = 0; - a->reservedR = 0; - - /* Sanity fixup... Shouldn't really happen. (AIA) */ - if (na->initialized_size > na->data_size) - na->initialized_size = na->data_size; - - /* Copy data from run list to resident attribute value. */ - bytes_read = ntfs_rl_pread(vol, na->rl, 0, na->initialized_size, - (u8*)a + val_ofs); - if (bytes_read != na->initialized_size) { - if (bytes_read < 0) - err = errno; - ntfs_log_trace("Eeek! Failed to read attribute data. Leaving " - "inconstant metadata. Run chkdsk. " - "Aborting...\n"); - errno = err; - return -1; - } - - /* Clear memory in gap between initialized_size and data_size. */ - if (na->initialized_size < na->data_size) - memset((u8*)a + val_ofs + na->initialized_size, 0, - na->data_size - na->initialized_size); - - /* - * Deallocate clusters from the runlist. - * - * NOTE: We can use ntfs_cluster_free() because we have already mapped - * the whole run list and thus it doesn't matter that the attribute - * record is in a transiently corrupted state at this moment in time. - */ - if (ntfs_cluster_free(vol, na, 0, -1) < 0) { - err = errno; - ntfs_log_perror("Eeek! Failed to release allocated clusters"); - ntfs_log_trace("Ignoring error and leaving behind wasted " - "clusters.\n"); - } - - /* Throw away the now unused runlist. */ - free(na->rl); - na->rl = NULL; - - /* Update in-memory struct ntfs_attr. */ - NAttrClearNonResident(na); - NAttrClearSparse(na); - NAttrClearEncrypted(na); - na->initialized_size = na->data_size; - na->allocated_size = na->compressed_size = (na->data_size + 7) & ~7; - na->compression_block_size = 0; - na->compression_block_size_bits = na->compression_block_clusters = 0; - return 0; -} - -/* - * If we are in the first extent, then set/clean sparse bit, - * update allocated and compressed size. - */ -static int ntfs_attr_update_meta(ATTR_RECORD *a, ntfs_attr *na, MFT_RECORD *m, - ntfs_attr_search_ctx *ctx) -{ - int sparse, ret = 0; - - ntfs_log_trace("Entering for inode 0x%llx, attr 0x%x\n", - (unsigned long long)na->ni->mft_no, na->type); - - if (a->lowest_vcn) - goto out; - - a->allocated_size = cpu_to_sle64(na->allocated_size); - - /* Update sparse bit. */ - sparse = ntfs_rl_sparse(na->rl); - if (sparse == -1) { - errno = EIO; - goto error; - } - - /* Attribute become sparse. */ - if (sparse && !(a->flags & (ATTR_IS_SPARSE | ATTR_IS_COMPRESSED))) { - /* - * Move attribute to another mft record, if attribute is too - * small to add compressed_size field to it and we have no - * free space in the current mft record. - */ - if ((le32_to_cpu(a->length) - - le16_to_cpu(a->mapping_pairs_offset) == 8) - && !(le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use))) { - - if (!NInoAttrList(na->ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(na->ni)) - goto leave; - goto retry; - } - if (ntfs_attr_record_move_away(ctx, 8)) { - ntfs_log_perror("Failed to move attribute"); - goto error; - } - ntfs_attr_put_search_ctx(ctx); - goto retry; - } - if (!(le32_to_cpu(a->length) - le16_to_cpu( - a->mapping_pairs_offset))) { - errno = EIO; - ntfs_log_perror("Mapping pairs space is 0"); - goto error; - } - - NAttrSetSparse(na); - a->flags |= ATTR_IS_SPARSE; - a->compression_unit = STANDARD_COMPRESSION_UNIT; /* Windows - set it so, even if attribute is not actually compressed. */ - - memmove((u8*)a + le16_to_cpu(a->name_offset) + 8, - (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - - a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) + 8); - - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) + 8); - } - - /* Attribute no longer sparse. */ - if (!sparse && (a->flags & ATTR_IS_SPARSE) && - !(a->flags & ATTR_IS_COMPRESSED)) { - - NAttrClearSparse(na); - a->flags &= ~ATTR_IS_SPARSE; - a->compression_unit = 0; - - memmove((u8*)a + le16_to_cpu(a->name_offset) - 8, - (u8*)a + le16_to_cpu(a->name_offset), - a->name_length * sizeof(ntfschar)); - - if (le16_to_cpu(a->name_offset) >= 8) - a->name_offset = cpu_to_le16(le16_to_cpu(a->name_offset) - 8); - - a->mapping_pairs_offset = - cpu_to_le16(le16_to_cpu(a->mapping_pairs_offset) - 8); - } - - /* Update compressed size if required. */ - if (sparse || (na->data_flags & ATTR_COMPRESSION_MASK)) { - s64 new_compr_size; - - new_compr_size = ntfs_rl_get_compressed_size(na->ni->vol, na->rl); - if (new_compr_size == -1) - goto error; - - na->compressed_size = new_compr_size; - a->compressed_size = cpu_to_sle64(new_compr_size); - } - /* - * Set FILE_NAME dirty flag, to update sparse bit and - * allocated size in the index. - */ - if (na->type == AT_DATA && na->name == AT_UNNAMED) { - if (sparse) - na->ni->allocated_size = na->compressed_size; - else - na->ni->allocated_size = na->allocated_size; - NInoFileNameSetDirty(na->ni); - } -out: - return ret; -leave: ret = -1; goto out; /* return -1 */ -retry: ret = -2; goto out; -error: ret = -3; goto out; -} - -#define NTFS_VCN_DELETE_MARK -2 -/** - * ntfs_attr_update_mapping_pairs_i - see ntfs_attr_update_mapping_pairs - */ -static int ntfs_attr_update_mapping_pairs_i(ntfs_attr *na, VCN from_vcn) -{ - ntfs_attr_search_ctx *ctx; - ntfs_inode *ni, *base_ni; - MFT_RECORD *m; - ATTR_RECORD *a; - VCN stop_vcn; - const runlist_element *stop_rl; - int err, mp_size, cur_max_mp_size, exp_max_mp_size, ret = -1; - BOOL finished_build; - -retry: - if (!na || !na->rl || from_vcn) { - errno = EINVAL; - ntfs_log_perror("%s: na=%p", __FUNCTION__, na); - return -1; - } - - ntfs_log_trace("Entering for inode %llu, attr 0x%x\n", - (unsigned long long)na->ni->mft_no, na->type); - - if (!NAttrNonResident(na)) { - errno = EINVAL; - ntfs_log_perror("%s: resident attribute", __FUNCTION__); - return -1; - } - - if (na->ni->nr_extents == -1) - base_ni = na->ni->base_ni; - else - base_ni = na->ni; - - ctx = ntfs_attr_get_search_ctx(base_ni, NULL); - if (!ctx) - return -1; - - /* Fill attribute records with new mapping pairs. */ - stop_vcn = 0; - stop_rl = na->rl; - finished_build = FALSE; - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, from_vcn, NULL, 0, ctx)) { - a = ctx->attr; - m = ctx->mrec; - /* - * If runlist is updating not from the beginning, then set - * @stop_vcn properly, i.e. to the lowest vcn of record that - * contain @from_vcn. Also we do not need @from_vcn anymore, - * set it to 0 to make ntfs_attr_lookup enumerate attributes. - */ - if (from_vcn) { - LCN first_lcn; - - stop_vcn = sle64_to_cpu(a->lowest_vcn); - from_vcn = 0; - /* - * Check whether the first run we need to update is - * the last run in runlist, if so, then deallocate - * all attrubute extents starting this one. - */ - first_lcn = ntfs_rl_vcn_to_lcn(na->rl, stop_vcn); - if (first_lcn == LCN_EINVAL) { - errno = EIO; - ntfs_log_perror("Bad runlist"); - goto put_err_out; - } - if (first_lcn == LCN_ENOENT || - first_lcn == LCN_RL_NOT_MAPPED) - finished_build = TRUE; - } - - /* - * Check whether we finished mapping pairs build, if so mark - * extent as need to delete (by setting highest vcn to - * NTFS_VCN_DELETE_MARK (-2), we shall check it later and - * delete extent) and continue search. - */ - if (finished_build) { - ntfs_log_trace("Mark attr 0x%x for delete in inode " - "%lld.\n", (unsigned)le32_to_cpu(a->type), - (long long)ctx->ntfs_ino->mft_no); - a->highest_vcn = cpu_to_sle64(NTFS_VCN_DELETE_MARK); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - continue; - } - - switch (ntfs_attr_update_meta(a, na, m, ctx)) { - case -1: return -1; - case -2: goto retry; - case -3: goto put_err_out; - } - - /* - * Determine maximum possible length of mapping pairs, - * if we shall *not* expand space for mapping pairs. - */ - cur_max_mp_size = le32_to_cpu(a->length) - - le16_to_cpu(a->mapping_pairs_offset); - /* - * Determine maximum possible length of mapping pairs in the - * current mft record, if we shall expand space for mapping - * pairs. - */ - exp_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) + cur_max_mp_size; - /* Get the size for the rest of mapping pairs array. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, stop_rl, - stop_vcn, exp_max_mp_size); - if (mp_size <= 0) { - ntfs_log_perror("%s: get MP size failed", __FUNCTION__); - goto put_err_out; - } - /* Test mapping pairs for fitting in the current mft record. */ - if (mp_size > exp_max_mp_size) { - /* - * Mapping pairs of $ATTRIBUTE_LIST attribute must fit - * in the base mft record. Try to move out other - * attributes and try again. - */ - if (na->type == AT_ATTRIBUTE_LIST) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_free_space(na->ni, mp_size - - cur_max_mp_size)) { - ntfs_log_perror("Attribute list is too " - "big. Defragment the " - "volume\n"); - return -1; - } - goto retry; - } - - /* Add attribute list if it isn't present, and retry. */ - if (!NInoAttrList(base_ni)) { - ntfs_attr_put_search_ctx(ctx); - if (ntfs_inode_add_attrlist(base_ni)) { - ntfs_log_perror("Can not add attrlist"); - return -1; - } - goto retry; - } - - /* - * Set mapping pairs size to maximum possible for this - * mft record. We shall write the rest of mapping pairs - * to another MFT records. - */ - mp_size = exp_max_mp_size; - } - - /* Change space for mapping pairs if we need it. */ - if (((mp_size + 7) & ~7) != cur_max_mp_size) { - if (ntfs_attr_record_resize(m, a, - le16_to_cpu(a->mapping_pairs_offset) + - mp_size)) { - errno = EIO; - ntfs_log_perror("Failed to resize attribute"); - goto put_err_out; - } - } - - /* Update lowest vcn. */ - a->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_inode_mark_dirty(ctx->ntfs_ino); - if ((ctx->ntfs_ino->nr_extents == -1 || - NInoAttrList(ctx->ntfs_ino)) && - ctx->attr->type != AT_ATTRIBUTE_LIST) { - ctx->al_entry->lowest_vcn = cpu_to_sle64(stop_vcn); - ntfs_attrlist_mark_dirty(ctx->ntfs_ino); - } - - /* - * Generate the new mapping pairs array directly into the - * correct destination, i.e. the attribute record itself. - */ - if (!ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + le16_to_cpu( - a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_rl)) - finished_build = TRUE; - if (stop_rl) - stop_vcn = stop_rl->vcn; - else - stop_vcn = 0; - if (!finished_build && errno != ENOSPC) { - ntfs_log_perror("Failed to build mapping pairs"); - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - } - /* Check whether error occurred. */ - if (errno != ENOENT) { - ntfs_log_perror("%s: Attribute lookup failed", __FUNCTION__); - goto put_err_out; - } - - /* Deallocate not used attribute extents and return with success. */ - if (finished_build) { - ntfs_attr_reinit_search_ctx(ctx); - ntfs_log_trace("Deallocate marked extents.\n"); - while (!ntfs_attr_lookup(na->type, na->name, na->name_len, - CASE_SENSITIVE, 0, NULL, 0, ctx)) { - if (sle64_to_cpu(ctx->attr->highest_vcn) != - NTFS_VCN_DELETE_MARK) - continue; - /* Remove unused attribute record. */ - if (ntfs_attr_record_rm(ctx)) { - ntfs_log_perror("Could not remove unused attr"); - goto put_err_out; - } - ntfs_attr_reinit_search_ctx(ctx); - } - if (errno != ENOENT) { - ntfs_log_perror("%s: Attr lookup failed", __FUNCTION__); - goto put_err_out; - } - ntfs_log_trace("Deallocate done.\n"); - ntfs_attr_put_search_ctx(ctx); - goto ok; - } - ntfs_attr_put_search_ctx(ctx); - ctx = NULL; - - /* Allocate new MFT records for the rest of mapping pairs. */ - while (1) { - /* Calculate size of rest mapping pairs. */ - mp_size = ntfs_get_size_for_mapping_pairs(na->ni->vol, - na->rl, stop_vcn, INT_MAX); - if (mp_size <= 0) { - ntfs_log_perror("%s: get mp size failed", __FUNCTION__); - goto put_err_out; - } - /* Allocate new mft record. */ - ni = ntfs_mft_record_alloc(na->ni->vol, base_ni); - if (!ni) { - ntfs_log_perror("Could not allocate new MFT record"); - goto put_err_out; - } - m = ni->mrec; - /* - * If mapping size exceed available space, set them to - * possible maximum. - */ - cur_max_mp_size = le32_to_cpu(m->bytes_allocated) - - le32_to_cpu(m->bytes_in_use) - - (offsetof(ATTR_RECORD, compressed_size) + - (((na->data_flags & ATTR_COMPRESSION_MASK) - || NAttrSparse(na)) ? - sizeof(a->compressed_size) : 0)) - - ((sizeof(ntfschar) * na->name_len + 7) & ~7); - if (mp_size > cur_max_mp_size) - mp_size = cur_max_mp_size; - /* Add attribute extent to new record. */ - err = ntfs_non_resident_attr_record_add(ni, na->type, - na->name, na->name_len, stop_vcn, mp_size, - na->data_flags); - if (err == -1) { - err = errno; - ntfs_log_perror("Could not add attribute extent"); - if (ntfs_mft_record_free(na->ni->vol, ni)) - ntfs_log_perror("Could not free MFT record"); - errno = err; - goto put_err_out; - } - a = (ATTR_RECORD*)((u8*)m + err); - - err = ntfs_mapping_pairs_build(na->ni->vol, (u8*)a + - le16_to_cpu(a->mapping_pairs_offset), mp_size, na->rl, - stop_vcn, &stop_rl); - if (stop_rl) - stop_vcn = stop_rl->vcn; - else - stop_vcn = 0; - if (err < 0 && errno != ENOSPC) { - err = errno; - ntfs_log_perror("Failed to build MP"); - if (ntfs_mft_record_free(na->ni->vol, ni)) - ntfs_log_perror("Couldn't free MFT record"); - errno = err; - goto put_err_out; - } - a->highest_vcn = cpu_to_sle64(stop_vcn - 1); - ntfs_inode_mark_dirty(ni); - /* All mapping pairs has been written. */ - if (!err) - break; - } -ok: - ret = 0; -out: - return ret; -put_err_out: - if (ctx) - ntfs_attr_put_search_ctx(ctx); - goto out; -} -#undef NTFS_VCN_DELETE_MARK - -/** - * ntfs_attr_update_mapping_pairs - update mapping pairs for ntfs attribute - * @na: non-resident ntfs open attribute for which we need update - * @from_vcn: update runlist starting this VCN - * - * Build mapping pairs from @na->rl and write them to the disk. Also, this - * function updates sparse bit, allocated and compressed size (allocates/frees - * space for this field if required). - * - * @na->allocated_size should be set to correct value for the new runlist before - * call to this function. Vice-versa @na->compressed_size will be calculated and - * set to correct value during this function. - * - * FIXME: This function does not update sparse bit and compressed size correctly - * if called with @from_vcn != 0. - * - * FIXME: Rewrite without using NTFS_VCN_DELETE_MARK define. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * EINVAL - Invalid arguments passed. - * ENOMEM - Not enough memory to complete operation. - * ENOSPC - There is no enough space in base mft to resize $ATTRIBUTE_LIST - * or there is no free MFT records left to allocate. - */ -int ntfs_attr_update_mapping_pairs(ntfs_attr *na, VCN from_vcn) -{ - int ret; - - ntfs_log_enter("Entering\n"); - ret = ntfs_attr_update_mapping_pairs_i(na, from_vcn); - ntfs_log_leave("\n"); - return ret; -} - -/** - * ntfs_non_resident_attr_shrink - shrink a non-resident, open ntfs attribute - * @na: non-resident ntfs attribute to shrink - * @newsize: new size (in bytes) to which to shrink the attribute - * - * Reduce the size of a non-resident, open ntfs attribute @na to @newsize bytes. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * ERANGE - @newsize is not valid for the attribute type of @na. - */ -static int ntfs_non_resident_attr_shrink(ntfs_attr *na, const s64 newsize) -{ - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - VCN first_free_vcn; - s64 nr_freed_clusters; - int err; - - ntfs_log_trace("Inode 0x%llx attr 0x%x new size %lld\n", (unsigned long long) - na->ni->mft_no, na->type, (long long)newsize); - - vol = na->ni->vol; - - /* - * Check the attribute type and the corresponding minimum size - * against @newsize and fail if @newsize is too small. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - if (errno == ERANGE) { - ntfs_log_trace("Eeek! Size bounds check failed. " - "Aborting...\n"); - } else if (errno == ENOENT) - errno = EIO; - return -1; - } - - /* The first cluster outside the new allocation. */ - 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. - */ - if ((na->allocated_size >> vol->cluster_size_bits) != first_free_vcn) { - if (ntfs_attr_map_whole_runlist(na)) { - ntfs_log_trace("Eeek! ntfs_attr_map_whole_runlist " - "failed.\n"); - return -1; - } - /* Deallocate all clusters starting with the first free one. */ - nr_freed_clusters = ntfs_cluster_free(vol, na, first_free_vcn, - -1); - if (nr_freed_clusters < 0) { - ntfs_log_trace("Eeek! Freeing of clusters failed. " - "Aborting...\n"); - return -1; - } - - /* Truncate the runlist itself. */ - if (ntfs_rl_truncate(&na->rl, first_free_vcn)) { - /* - * Failed to truncate the runlist, so just throw it - * away, it will be mapped afresh on next use. - */ - free(na->rl); - na->rl = NULL; - ntfs_log_trace("Eeek! Run list truncation failed.\n"); - return -1; - } - - /* Prepare to mapping pairs update. */ - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*first_free_vcn*/)) { - ntfs_log_trace("Eeek! Mapping pairs update failed. " - "Leaving inconstant metadata. " - "Run chkdsk.\n"); - return -1; - } - } - - /* Get the first attribute record. */ - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) - return -1; - - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - if (err == ENOENT) - err = EIO; - ntfs_log_trace("Eeek! Lookup of first attribute extent failed. " - "Leaving inconstant metadata.\n"); - goto put_err_out; - } - - /* Update data and initialized size. */ - na->data_size = newsize; - ctx->attr->data_size = cpu_to_sle64(newsize); - if (newsize < na->initialized_size) { - na->initialized_size = 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 the attribute now has zero size, make it resident. */ - if (!newsize) { - if (ntfs_attr_make_resident(na, ctx)) { - /* If couldn't make resident, just continue. */ - if (errno != EPERM) - ntfs_log_error("Failed to make attribute " - "resident. Leaving as is...\n"); - } - } - - /* Set the inode dirty so it is written out later. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ - ntfs_attr_put_search_ctx(ctx); - return 0; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - -/** - * ntfs_non_resident_attr_expand - expand a non-resident, open ntfs attribute - * @na: non-resident ntfs attribute to expand - * @newsize: new size (in bytes) to which to expand the attribute - * - * Expand the size of a non-resident, open ntfs attribute @na to @newsize bytes, - * by allocating new clusters. - * - * On success return 0 and on error return -1 with errno set to the error code. - * The following error codes are defined: - * ENOMEM - Not enough memory to complete operation. - * 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_non_resident_attr_expand_i(ntfs_attr *na, const s64 newsize) -{ - LCN lcn_seek_from; - VCN first_free_vcn; - ntfs_volume *vol; - ntfs_attr_search_ctx *ctx; - runlist *rl, *rln; - s64 org_alloc_size; - int err; - - ntfs_log_trace("Inode %lld, attr 0x%x, new size %lld old size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize, (long long)na->data_size); - - vol = na->ni->vol; - - /* - * Check the attribute type and the corresponding maximum size - * against @newsize and fail if @newsize is too big. - */ - if (ntfs_attr_size_bounds_check(vol, na->type, newsize) < 0) { - if (errno == ENOENT) - errno = EIO; - ntfs_log_perror("%s: bounds check failed", __FUNCTION__); - return -1; - } - - /* Save for future use. */ - org_alloc_size = na->allocated_size; - /* The first cluster outside the new allocation. */ - first_free_vcn = (newsize + vol->cluster_size - 1) >> - vol->cluster_size_bits; - /* - * Compare the new allocation with the old one and only allocate - * clusters if there is a change. - */ - if ((na->allocated_size >> vol->cluster_size_bits) < first_free_vcn) { - if (ntfs_attr_map_whole_runlist(na)) { - ntfs_log_perror("ntfs_attr_map_whole_runlist failed"); - return -1; - } - - /* - * If we extend $DATA attribute on NTFS 3+ volume, we can add - * sparse runs instead of real allocation of clusters. - */ - if (na->type == AT_DATA && vol->major_ver >= 3) { - rl = ntfs_malloc(0x1000); - if (!rl) - return -1; - - rl[0].vcn = (na->allocated_size >> - vol->cluster_size_bits); - rl[0].lcn = LCN_HOLE; - rl[0].length = first_free_vcn - - (na->allocated_size >> vol->cluster_size_bits); - rl[1].vcn = first_free_vcn; - rl[1].lcn = LCN_ENOENT; - rl[1].length = 0; - } else { - /* - * Determine first after last LCN of attribute. - * We will start seek clusters from this LCN to avoid - * fragmentation. If there are no valid LCNs in the - * attribute let the cluster allocator choose the - * starting LCN. - */ - lcn_seek_from = -1; - if (na->rl->length) { - /* Seek to the last run list element. */ - for (rl = na->rl; (rl + 1)->length; rl++) - ; - /* - * If the last LCN is a hole or similar seek - * back to last valid LCN. - */ - while (rl->lcn < 0 && rl != na->rl) - rl--; - /* - * Only set lcn_seek_from it the LCN is valid. - */ - if (rl->lcn >= 0) - lcn_seek_from = rl->lcn + rl->length; - } - - rl = ntfs_cluster_alloc(vol, na->allocated_size >> - vol->cluster_size_bits, first_free_vcn - - (na->allocated_size >> - vol->cluster_size_bits), lcn_seek_from, - DATA_ZONE); - if (!rl) { - ntfs_log_perror("Cluster allocation failed " - "(%lld)", - (long long)first_free_vcn - - ((long long)na->allocated_size >> - vol->cluster_size_bits)); - return -1; - } - } - - /* Append new clusters to attribute runlist. */ - rln = ntfs_runlists_merge(na->rl, rl); - if (!rln) { - /* Failed, free just allocated clusters. */ - err = errno; - ntfs_log_perror("Run list merge failed"); - ntfs_cluster_free_from_rl(vol, rl); - free(rl); - errno = err; - return -1; - } - na->rl = rln; - - /* Prepare to mapping pairs update. */ - na->allocated_size = first_free_vcn << vol->cluster_size_bits; - /* Write mapping pairs for new runlist. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> - vol->cluster_size_bits*/)) { - err = errno; - ntfs_log_perror("Mapping pairs update failed"); - goto rollback; - } - } - - ctx = ntfs_attr_get_search_ctx(na->ni, NULL); - if (!ctx) { - err = errno; - if (na->allocated_size == org_alloc_size) { - errno = err; - return -1; - } else - goto rollback; - } - - if (ntfs_attr_lookup(na->type, na->name, na->name_len, CASE_SENSITIVE, - 0, NULL, 0, ctx)) { - err = errno; - ntfs_log_perror("Lookup of first attribute extent failed"); - if (err == ENOENT) - err = EIO; - if (na->allocated_size != org_alloc_size) { - ntfs_attr_put_search_ctx(ctx); - goto rollback; - } else - goto put_err_out; - } - - /* Update data size. */ - 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); - } - /* Set the inode dirty so it is written out later. */ - ntfs_inode_mark_dirty(ctx->ntfs_ino); - /* Done! */ - ntfs_attr_put_search_ctx(ctx); - return 0; -rollback: - /* Free allocated clusters. */ - if (ntfs_cluster_free(vol, na, org_alloc_size >> - vol->cluster_size_bits, -1) < 0) { - err = EIO; - ntfs_log_perror("Leaking clusters"); - } - /* Now, truncate the runlist itself. */ - if (ntfs_rl_truncate(&na->rl, org_alloc_size >> - vol->cluster_size_bits)) { - /* - * Failed to truncate the runlist, so just throw it away, it - * will be mapped afresh on next use. - */ - free(na->rl); - na->rl = NULL; - ntfs_log_perror("Couldn't truncate runlist. Rollback failed"); - } else { - /* Prepare to mapping pairs update. */ - na->allocated_size = org_alloc_size; - /* Restore mapping pairs. */ - if (ntfs_attr_update_mapping_pairs(na, 0 /*na->allocated_size >> - vol->cluster_size_bits*/)) { - ntfs_log_perror("Failed to restore old mapping pairs"); - } - } - errno = err; - return -1; -put_err_out: - ntfs_attr_put_search_ctx(ctx); - errno = err; - return -1; -} - - -static int ntfs_non_resident_attr_expand(ntfs_attr *na, const s64 newsize) -{ - int ret; - - ntfs_log_enter("Entering\n"); - ret = ntfs_non_resident_attr_expand_i(na, newsize); - ntfs_log_leave("\n"); - return ret; -} - -/** - * ntfs_attr_truncate - resize an ntfs attribute - * @na: open ntfs attribute to resize - * @newsize: new size (in bytes) to which to resize the attribute - * - * Change the size of an open ntfs attribute @na to @newsize bytes. If the - * attribute is made bigger and the attribute is resident the newly - * "allocated" space is cleared and if the attribute is non-resident the - * newly allocated space is marked as not initialised and no real allocation - * on disk is performed. - * - * On success return 0. - * On error return values are: - * STATUS_RESIDENT_ATTRIBUTE_FILLED_MFT - * STATUS_ERROR - otherwise - * The following error codes are defined: - * EINVAL - Invalid arguments were passed to the function. - * EOPNOTSUPP - The desired resize is not implemented yet. - * EACCES - Encrypted attribute. - */ -int ntfs_attr_truncate(ntfs_attr *na, const s64 newsize) -{ - int ret = STATUS_ERROR; - s64 fullsize; - BOOL compressed; - - if (!na || newsize < 0 || - (na->ni->mft_no == FILE_MFT && na->type == AT_DATA)) { - ntfs_log_trace("Invalid arguments passed.\n"); - errno = EINVAL; - return STATUS_ERROR; - } - - ntfs_log_enter("Entering for inode %lld, attr 0x%x, size %lld\n", - (unsigned long long)na->ni->mft_no, na->type, - (long long)newsize); - - if (na->data_size == newsize) { - ntfs_log_trace("Size is already ok\n"); - ret = STATUS_OK; - goto out; - } - /* - * Encrypted attributes are not supported. We return access denied, - * which is what Windows NT4 does, too. - */ - if (na->data_flags & ATTR_IS_ENCRYPTED) { - errno = EACCES; - ntfs_log_info("Failed to truncate encrypted attribute"); - goto out; - } - /* - * TODO: Implement making handling of compressed attributes. - * Currently we can only expand the attribute or delete it, - * and only for ATTR_IS_COMPRESSED. This is however possible - * for resident attributes when there is no open fuse context - * (important case : $INDEX_ROOT:$I30) - */ - compressed = (na->data_flags & ATTR_COMPRESSION_MASK) - != const_cpu_to_le16(0); - if (compressed - && NAttrNonResident(na) - && (((na->data_flags & ATTR_COMPRESSION_MASK) != ATTR_IS_COMPRESSED) - || (newsize && (newsize < na->data_size)))) { - errno = EOPNOTSUPP; - ntfs_log_perror("Failed to truncate compressed attribute"); - goto out; - } - if (NAttrNonResident(na)) { - /* - * For compressed data, the last block must be fully - * allocated, and we do not known 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 - * unless being able to face the consequences ! - */ - if (compressed && newsize) - fullsize = (na->initialized_size - | (na->compression_block_size - 1)) + 1; - else - fullsize = newsize; - if (fullsize > na->data_size) - ret = ntfs_non_resident_attr_expand(na, fullsize); - else - ret = ntfs_non_resident_attr_shrink(na, fullsize); - } else - ret = ntfs_resident_attr_resize(na, newsize); -out: - ntfs_log_leave("Return status %d\n", ret); - return ret; -} - -/* - * Stuff a hole in a compressed file - * - * An unallocated hole must be aligned on compression block size. - * If needed current block and target block are stuffed with zeroes. - * - * Returns 0 if succeeded, - * -1 if it failed (as explained in errno) - */ - -static int stuff_hole(ntfs_attr *na, const s64 pos) -{ - s64 size; - s64 begin_size; - s64 end_size; - char *buf; - int ret; - - ret = 0; - /* - * If the attribute is resident, the compression block size - * is not defined yet and we can make no decision. - * So we first try resizing to the target and if the - * attribute is still resident, we're done - */ - if (!NAttrNonResident(na)) { - ret = ntfs_resident_attr_resize(na, pos); - if (!ret && !NAttrNonResident(na)) - na->initialized_size = na->data_size = pos; - } - if (!ret && NAttrNonResident(na)) { - /* does the hole span over several compression block ? */ - if ((pos ^ na->initialized_size) - & ~(na->compression_block_size - 1)) { - begin_size = ((na->initialized_size - 1) - | (na->compression_block_size - 1)) - + 1 - na->initialized_size; - end_size = pos & (na->compression_block_size - 1); - size = (begin_size > end_size ? begin_size : end_size); - } else { - /* short stuffing in a single compression block */ - begin_size = size = pos - na->initialized_size; - end_size = 0; - } - if (size) - buf = (char*)ntfs_malloc(size); - else - buf = (char*)NULL; - if (buf || !size) { - memset(buf,0,size); - /* stuff into current block */ - if (begin_size - && (ntfs_attr_pwrite(na, - na->initialized_size, begin_size, buf) - != begin_size)) - ret = -1; - /* create an unstuffed hole */ - if (!ret - && ((na->initialized_size + end_size) < pos) - && ntfs_non_resident_attr_expand(na, - pos - end_size)) - ret = -1; - else - na->initialized_size - = na->data_size = pos - end_size; - /* stuff into the target block */ - if (!ret && end_size - && (ntfs_attr_pwrite(na, - na->initialized_size, end_size, buf) - != end_size)) - ret = -1; - if (buf) - free(buf); - } else - ret = -1; - } - /* make absolutely sure we have reached the target */ - if (!ret && (na->initialized_size != pos)) { - ntfs_log_error("Failed to stuff a compressed file" - "target %lld reached %lld\n", - (long long)pos, (long long)na->initialized_size); - errno = EIO; - ret = -1; - } - return (ret); -} - -/** - * ntfs_attr_readall - read the entire data from an ntfs attribute - * @ni: open ntfs inode in which the ntfs attribute resides - * @type: attribute type - * @name: attribute name in little endian Unicode or AT_UNNAMED or NULL - * @name_len: length of attribute @name in Unicode characters (if @name given) - * @data_size: if non-NULL then store here the data size - * - * This function will read the entire content of an ntfs attribute. - * If @name is AT_UNNAMED then look specifically for an unnamed attribute. - * If @name is NULL then the attribute could be either named or not. - * In both those cases @name_len is not used at all. - * - * On success a buffer is allocated with the content of the attribute - * and which needs to be freed when it's not needed anymore. If the - * @data_size parameter is non-NULL then the data size is set there. - * - * On error NULL is returned with errno set to the error code. - */ -void *ntfs_attr_readall(ntfs_inode *ni, const ATTR_TYPES type, - ntfschar *name, u32 name_len, s64 *data_size) -{ - ntfs_attr *na; - void *data, *ret = NULL; - s64 size; - - ntfs_log_enter("Entering\n"); - - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - ntfs_log_perror("ntfs_attr_open failed"); - goto err_exit; - } - data = ntfs_malloc(na->data_size); - if (!data) - goto out; - - size = ntfs_attr_pread(na, 0, na->data_size, data); - if (size != na->data_size) { - ntfs_log_perror("ntfs_attr_pread failed"); - free(data); - goto out; - } - ret = data; - if (data_size) - *data_size = size; -out: - ntfs_attr_close(na); -err_exit: - ntfs_log_leave("\n"); - return ret; -} - - - -int ntfs_attr_exist(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, - u32 name_len) -{ - ntfs_attr_search_ctx *ctx; - int ret; - - ntfs_log_trace("Entering\n"); - - ctx = ntfs_attr_get_search_ctx(ni, NULL); - if (!ctx) - return 0; - - ret = ntfs_attr_lookup(type, name, name_len, CASE_SENSITIVE, 0, NULL, 0, - ctx); - - ntfs_attr_put_search_ctx(ctx); - - return !ret; -} - -int ntfs_attr_remove(ntfs_inode *ni, const ATTR_TYPES type, ntfschar *name, - u32 name_len) -{ - ntfs_attr *na; - int ret; - - ntfs_log_trace("Entering\n"); - - if (!ni) { - ntfs_log_error("%s: NULL inode pointer", __FUNCTION__); - errno = EINVAL; - return -1; - } - - na = ntfs_attr_open(ni, type, name, name_len); - if (!na) { - /* do not log removal of non-existent stream */ - if (type != AT_DATA) { - ntfs_log_perror("Failed to open attribute 0x%02x of inode " - "0x%llx", type, (unsigned long long)ni->mft_no); - } - return -1; - } - - ret = ntfs_attr_rm(na); - if (ret) - ntfs_log_perror("Failed to remove attribute 0x%02x of inode " - "0x%llx", type, (unsigned long long)ni->mft_no); - ntfs_attr_close(na); - - return ret; -} - -/* Below macros are 32-bit ready. */ -#define BCX(x) ((x) - (((x) >> 1) & 0x77777777) - \ - (((x) >> 2) & 0x33333333) - \ - (((x) >> 3) & 0x11111111)) -#define BITCOUNT(x) (((BCX(x) + (BCX(x) >> 4)) & 0x0F0F0F0F) % 255) - -static u8 *ntfs_init_lut256(void) -{ - int i; - u8 *lut; - - lut = ntfs_malloc(256); - if (lut) - for(i = 0; i < 256; i++) - *(lut + i) = 8 - BITCOUNT(i); - return lut; -} - -s64 ntfs_attr_get_free_bits(ntfs_attr *na) -{ - u8 *buf, *lut; - s64 br = 0; - s64 total = 0; - s64 nr_free = 0; - - lut = ntfs_init_lut256(); - if (!lut) - return -1; - - buf = ntfs_malloc(65536); - if (!buf) - goto out; - - while (1) { - u32 *p; - br = ntfs_attr_pread(na, total, 65536, buf); - if (br <= 0) - break; - total += br; - p = (u32 *)buf + br / 4 - 1; - for (; (u8 *)p >= buf; p--) { - nr_free += lut[ *p & 255] + - lut[(*p >> 8) & 255] + - lut[(*p >> 16) & 255] + - lut[(*p >> 24) ]; - } - switch (br % 4) { - case 3: nr_free += lut[*(buf + br - 3)]; - case 2: nr_free += lut[*(buf + br - 2)]; - case 1: nr_free += lut[*(buf + br - 1)]; - } - } - free(buf); -out: - free(lut); - if (!total || br < 0) - return -1; - return nr_free; -} - -#endif -